vdoc: fix sorting; fix missing symbols; document functions (#7161)
parent
dcca821000
commit
2ba8d31118
|
@ -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)
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue