vdoc: fix sorting; fix missing symbols; document functions (#7161)

pull/7172/head
Ned Palacios 2020-12-07 09:43:25 +08:00 committed by GitHub
parent dcca821000
commit 2ba8d31118
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 85 additions and 52 deletions

View File

@ -154,9 +154,7 @@ fn (mut cfg DocConfig) serve_html() {
}
def_name := docs.keys()[0]
server_url := 'http://localhost:' + cfg.server_port.str()
server := net.listen_tcp(cfg.server_port) or {
panic(err)
}
server := net.listen_tcp(cfg.server_port) or { panic(err) }
println('Serving docs on: $server_url')
if cfg.open_docs {
open_url(server_url)
@ -178,9 +176,7 @@ fn (mut cfg DocConfig) serve_html() {
panic(err)
}
handle_http_connection(mut conn, server_context)
conn.close() or {
eprintln('error closing the connection: $err')
}
conn.close() or { eprintln('error closing the connection: $err') }
}
}
@ -191,9 +187,7 @@ struct VdocHttpServerContext {
}
fn handle_http_connection(mut con net.TcpConn, ctx &VdocHttpServerContext) {
mut reader := io.new_buffered_reader({
reader: io.make_reader(con)
})
mut reader := io.new_buffered_reader(reader: io.make_reader(con))
first_line := reader.read_line() or {
send_http_response(mut con, 501, ctx.content_type, 'bad request')
return
@ -230,15 +224,11 @@ fn send_http_response(mut con net.TcpConn, http_code int, content_type string, h
http_response.write('\r\n')
http_response.write(html)
sresponse := http_response.str()
con.write_str(sresponse) or {
eprintln('error sending http response: $err')
}
con.write_str(sresponse) or { eprintln('error sending http response: $err') }
}
fn get_src_link(repo_url string, file_name string, line_nr int) string {
mut url := urllib.parse(repo_url) or {
return ''
}
mut url := urllib.parse(repo_url) or { return '' }
if url.path.len <= 1 || file_name.len == 0 {
return ''
}
@ -656,13 +646,13 @@ fn (mut cfg DocConfig) render_static() {
return
}
cfg.assets = {
'doc_css': cfg.get_resource(css_js_assets[0], true)
'doc_css': cfg.get_resource(css_js_assets[0], true)
'normalize_css': cfg.get_resource(css_js_assets[1], true)
'doc_js': cfg.get_resource(css_js_assets[2], !cfg.serve_http)
'light_icon': cfg.get_resource('light.svg', true)
'dark_icon': cfg.get_resource('dark.svg', true)
'menu_icon': cfg.get_resource('menu.svg', true)
'arrow_icon': cfg.get_resource('arrow.svg', true)
'doc_js': cfg.get_resource(css_js_assets[2], !cfg.serve_http)
'light_icon': cfg.get_resource('light.svg', true)
'dark_icon': cfg.get_resource('dark.svg', true)
'menu_icon': cfg.get_resource('menu.svg', true)
'arrow_icon': cfg.get_resource('arrow.svg', true)
}
}
@ -679,9 +669,7 @@ fn (cfg DocConfig) get_readme(path string) string {
}
readme_path := os.join_path(path, '${fname}.md')
cfg.vprintln('Reading README file from $readme_path')
readme_contents := os.read_file(readme_path) or {
''
}
readme_contents := os.read_file(readme_path) or { '' }
return readme_contents
}
@ -809,16 +797,12 @@ fn (mut cfg DocConfig) generate_docs_from_file() {
cfg.output_path = os.real_path('.')
}
if !os.exists(cfg.output_path) {
os.mkdir(cfg.output_path) or {
panic(err)
}
os.mkdir(cfg.output_path) or { panic(err) }
}
if cfg.is_multi {
cfg.output_path = os.join_path(cfg.output_path, '_docs')
if !os.exists(cfg.output_path) {
os.mkdir(cfg.output_path) or {
panic(err)
}
os.mkdir(cfg.output_path) or { panic(err) }
} else {
for fname in css_js_assets {
os.rm(os.join_path(cfg.output_path, fname))
@ -865,9 +849,7 @@ fn get_ignore_paths(path string) ?[]string {
}
res = final.map(os.join_path(path, it.trim_right('/')))
} else {
mut dirs := os.ls(path) or {
return []string{}
}
mut dirs := os.ls(path) or { return []string{} }
res = dirs.map(os.join_path(path, it)).filter(os.is_dir(it))
}
return res.map(it.replace('/', os.path_separator))
@ -887,12 +869,8 @@ fn is_included(path string, ignore_paths []string) bool {
}
fn get_modules_list(path string, ignore_paths2 []string) []string {
files := os.ls(path) or {
return []string{}
}
mut ignore_paths := get_ignore_paths(path) or {
[]string{}
}
files := os.ls(path) or { return []string{} }
mut ignore_paths := get_ignore_paths(path) or { []string{} }
ignore_paths << ignore_paths2
mut dirs := []string{}
for file in files {
@ -912,9 +890,7 @@ fn get_modules_list(path string, ignore_paths2 []string) []string {
fn (cfg DocConfig) get_resource(name string, minify bool) string {
path := os.join_path(res_path, name)
mut res := os.read_file(path) or {
panic('vdoc: could not read $path')
}
mut res := os.read_file(path) or { panic('vdoc: could not read $path') }
if minify {
if name.ends_with('.js') {
res = js_compress(res)

View File

@ -18,6 +18,7 @@ const (
'nonexistant',
]
vfmt_verify_list = [
'cmd/tools/vdoc.v'
'cmd/v/v.v',
'vlib/builtin/array.v',
'vlib/os/file.v',

View File

@ -11,7 +11,8 @@ import v.scanner
import v.table
import v.util
// intentionally in order as a guide when arranging the docnodes
// SymbolKind categorizes the symbols it documents.
// The names are intentionally not in order as a guide when sorting the nodes.
pub enum SymbolKind {
none_
const_group
@ -75,10 +76,12 @@ pub mut:
parent_name string
return_type string
children []DocNode
attrs map[string]string
attrs map[string]string [json: attributes]
from_scope bool
is_pub bool [json: public]
}
// new_vdoc_preferences creates a new instance of pref.Preferences tailored for v.doc.
pub fn new_vdoc_preferences() &pref.Preferences {
// vdoc should be able to parse as much user code as possible
// so its preferences should be permissive:
@ -87,6 +90,7 @@ pub fn new_vdoc_preferences() &pref.Preferences {
}
}
// new creates a new instance of a `Doc` struct.
pub fn new(input_path string) Doc {
mut d := Doc{
base_path: os.real_path(input_path)
@ -105,6 +109,9 @@ pub fn new(input_path string) Doc {
return d
}
// stmt reads the data of an `ast.Stmt` node and returns a `DocNode`.
// An option error is thrown if the symbol is not exposed to the public
// (when `pub_only` is enabled) or the content's of the AST node is empty.
pub fn (mut d Doc) stmt(stmt ast.Stmt, filename string) ?DocNode {
mut node := DocNode{
name: d.stmt_name(stmt)
@ -112,9 +119,10 @@ pub fn (mut d Doc) stmt(stmt ast.Stmt, filename string) ?DocNode {
comment: ''
pos: d.convert_pos(filename, stmt.position())
file_path: os.join_path(d.base_path, filename)
is_pub: d.stmt_pub(stmt)
}
if (!node.content.starts_with('pub') && d.pub_only) || stmt is ast.GlobalDecl {
return error('symbol not public')
if (!node.is_pub && d.pub_only) || stmt is ast.GlobalDecl {
return error('symbol $node.name not public')
}
if node.name.starts_with(d.orig_mod_name + '.') {
node.name = node.name.all_after(d.orig_mod_name + '.')
@ -205,6 +213,7 @@ pub fn (mut d Doc) stmt(stmt ast.Stmt, filename string) ?DocNode {
return node
}
// file_ast reads the contents of `ast.File` and returns a map of `DocNode`s.
pub fn (mut d Doc) file_ast(file_ast ast.File) map[string]DocNode {
mut contents := map[string]DocNode{}
stmts := file_ast.stmts
@ -220,7 +229,6 @@ pub fn (mut d Doc) file_ast(file_ast ast.File) map[string]DocNode {
mut prev_comments := []ast.Comment{}
mut imports_section := true
for sidx, stmt in stmts {
// eprintln('stmt typeof: ' + typeof(stmt))
if stmt is ast.ExprStmt {
if stmt.expr is ast.Comment {
prev_comments << stmt.expr
@ -302,6 +310,8 @@ pub fn (mut d Doc) file_ast(file_ast ast.File) map[string]DocNode {
return contents
}
// file_ast_with_pos has the same function as the `file_ast` but
// instead returns a list of variables in a given offset-based position.
pub fn (mut d Doc) file_ast_with_pos(file_ast ast.File, pos int) map[string]DocNode {
lscope := file_ast.scope.innermost(pos)
mut contents := map[string]DocNode{}
@ -323,6 +333,8 @@ pub fn (mut d Doc) file_ast_with_pos(file_ast ast.File, pos int) map[string]DocN
return contents
}
// generate is a `Doc` method that will start documentation
// process based on a file path provided.
pub fn (mut d Doc) generate() ? {
// get all files
d.base_path = if os.is_dir(d.base_path) { d.base_path } else { os.real_path(os.dir(d.base_path)) }
@ -353,6 +365,8 @@ pub fn (mut d Doc) generate() ? {
return d.file_asts(file_asts)
}
// file_asts has the same function as the `file_ast` function but
// accepts an array of `ast.File` and throws an error if necessary.
pub fn (mut d Doc) file_asts(file_asts []ast.File) ? {
mut fname_has_set := false
d.orig_mod_name = file_asts[0].mod.name
@ -380,17 +394,27 @@ pub fn (mut d Doc) file_asts(file_asts []ast.File) ? {
}
contents := d.file_ast(file_ast)
for name, node in contents {
if name in d.contents && (d.contents[name].kind != .none_ || node.kind == .none_) {
d.contents[name].children << node.children
d.contents[name].children.sort_by_name()
if name !in d.contents {
d.contents[name] = node
continue
}
d.contents[name] = node
if d.contents[name].kind == .typedef && node.kind !in [.typedef, .none_] {
old_children := d.contents[name].children.clone()
d.contents[name] = node
d.contents[name].children = old_children
}
if d.contents[name].kind != .none_ || node.kind == .none_ {
d.contents[name].children << node.children
d.contents[name].children.sort_by_name()
d.contents[name].children.sort_by_kind()
}
}
}
d.time_generated = time.now()
}
// generate documents a certain file directory and returns an
// instance of `Doc` if it is successful. Otherwise, it will throw an error.
pub fn generate(input_path string, pub_only bool, with_comments bool) ?Doc {
mut doc := new(input_path)
doc.pub_only = pub_only
@ -399,6 +423,8 @@ pub fn generate(input_path string, pub_only bool, with_comments bool) ?Doc {
return doc
}
// generate_with_pos has the same function as the `generate` function but
// accepts an offset-based position and enables the comments by default.
pub fn generate_with_pos(input_path string, filename string, pos int) ?Doc {
mut doc := new(input_path)
doc.pub_only = false

View File

@ -6,7 +6,7 @@ import v.parser
import v.ast
import v.pref
// get_parent_mod - return the parent mod name, in dot format.
// get_parent_mod returns the parent mod name, in dot format.
// It works by climbing up the folder hierarchy, until a folder,
// that either contains main .v files, or a v.mod file is reached.
// For example, given something like /languages/v/vlib/x/websocket/tests/autobahn
@ -55,6 +55,8 @@ fn get_parent_mod(input_dir string) ?string {
return file_ast.mod.name
}
// lookup_module_with_path looks up the path of a given module name.
// Throws an error if the module was not found.
pub fn lookup_module_with_path(mod string, base_path string) ?string {
vexe := pref.vexe_path()
vroot := os.dir(vexe)
@ -76,10 +78,13 @@ pub fn lookup_module_with_path(mod string, base_path string) ?string {
return error('module "$mod" not found.')
}
// lookup_module returns the result of the `lookup_module_with_path`
// but with the current directory as the provided base lookup path.
pub fn lookup_module(mod string) ?string {
return lookup_module_with_path(mod, os.dir('.'))
}
// generate_from_mod generates a documentation from a specific module.
pub fn generate_from_mod(module_name string, pub_only bool, with_comments bool) ?Doc {
mod_path := lookup_module(module_name) ?
return generate(mod_path, pub_only, with_comments)

View File

@ -10,10 +10,12 @@ pub fn (nodes []DocNode) find(symname string) ?DocNode {
return error('symbol not found')
}
// sort_by_name sorts the array based on the symbol names.
pub fn (mut nodes []DocNode) sort_by_name() {
nodes.sort_with_compare(compare_nodes_by_name)
}
// sort_by_kind sorts the array based on the symbol kind.
pub fn (mut nodes []DocNode) sort_by_kind() {
nodes.sort_with_compare(compare_nodes_by_kind)
}
@ -36,6 +38,7 @@ fn compare_nodes_by_name(a &DocNode, b &DocNode) int {
return compare_strings(al, bl)
}
// arr() converts the map into an array of `DocNode`.
pub fn (cnts map[string]DocNode) arr() []DocNode {
mut contents := cnts.keys().map(cnts[it])
contents.sort_by_name()

View File

@ -7,6 +7,7 @@ import v.token
import v.table
import os
// merge_comments merges all the comment contents into a single text.
pub fn merge_comments(comments []ast.Comment) string {
mut res := []string{}
for comment in comments {
@ -15,6 +16,8 @@ pub fn merge_comments(comments []ast.Comment) string {
return res.join('\n')
}
// get_comment_block_right_before merges all the comments starting from
// the last up to the first item of the array.
pub fn get_comment_block_right_before(comments []ast.Comment) string {
if comments.len == 0 {
return ''
@ -60,6 +63,7 @@ pub fn get_comment_block_right_before(comments []ast.Comment) string {
return comment
}
// convert_pos converts the `token.Position` data into a `DocPos`.
fn (mut d Doc) convert_pos(filename string, pos token.Position) DocPos {
if filename !in d.sources {
d.sources[filename] = util.read_file(os.join_path(d.base_path, filename)) or { '' }
@ -74,6 +78,7 @@ fn (mut d Doc) convert_pos(filename string, pos token.Position) DocPos {
}
}
// stmt_signature returns the signature of a given `ast.Stmt` node.
pub fn (mut d Doc) stmt_signature(stmt ast.Stmt) string {
match stmt {
ast.Module {
@ -90,6 +95,7 @@ pub fn (mut d Doc) stmt_signature(stmt ast.Stmt) string {
}
}
// stmt_name returns the name of a given `ast.Stmt` node.
pub fn (d Doc) stmt_name(stmt ast.Stmt) string {
match stmt {
ast.FnDecl, ast.StructDecl, ast.EnumDecl, ast.InterfaceDecl { return stmt.name }
@ -101,10 +107,26 @@ pub fn (d Doc) stmt_name(stmt ast.Stmt) string {
}
}
// stmt_pub returns a boolean if a given `ast.Stmt` node
// is exposed to the public.
pub fn (d Doc) stmt_pub(stmt ast.Stmt) bool {
match stmt {
ast.FnDecl, ast.StructDecl, ast.EnumDecl, ast.InterfaceDecl, ast.ConstDecl { return stmt.is_pub }
ast.TypeDecl { match stmt {
ast.FnTypeDecl, ast.AliasTypeDecl, ast.SumTypeDecl { return stmt.is_pub }
} }
else { return false }
}
}
// type_to_str is a wrapper function around `fmt.table.type_to_str`.
pub fn (d Doc) type_to_str(typ table.Type) string {
return d.fmt.table.type_to_str(typ).all_after('&')
}
// expr_typ_to_string has the same function as `Doc.typ_to_str`
// but for `ast.Expr` nodes. The checker will check first the
// node and it executes the `type_to_str` method.
pub fn (mut d Doc) expr_typ_to_string(ex ast.Expr) string {
expr_typ := d.checker.expr(ex)
return d.type_to_str(expr_typ)