vdoc: fix sorting + other minor improvements

pull/5387/head
Ned Palacios 2020-06-19 16:36:45 +08:00 committed by GitHub
parent 770132ff37
commit 5ff7d07138
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 270 additions and 180 deletions

View File

@ -275,10 +275,10 @@ body {
word-break: break-word;
}
.doc-content > .doc-node.const:not(:first-child) {
padding-top: 0;
padding-top: 4rem;
}
.doc-content > .doc-node.const:not(:last-child) {
padding-bottom: 1rem;
padding-bottom: 2rem;
}
.doc-content > .timestamp {
font-size: 0.8rem;
@ -556,7 +556,12 @@ pre {
.doc-nav .content.hidden {
display: flex;
}
.doc-content > .doc-node.const:not(:first-child) {
padding-top: 0;
}
.doc-content > .doc-node.const:not(:last-child) {
padding-bottom: 1rem;
}
.doc-container {
margin-top: 0;
margin-left: 300px;
@ -565,7 +570,6 @@ pre {
padding-top: 1rem !important;
margin-top: 0 !important;
}
.doc-toc {
top: 0;
}

View File

@ -36,6 +36,51 @@ const (
exe_dir = os.dir(exe_path)
res_path = os.join_path(exe_dir, 'vdoc-resources')
vexe_path = os.base_dir(@VEXE)
html_content = '
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }} | vdoc</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
{{ head_assets }}
</head>
<body>
<div id="page">
<header class="doc-nav hidden">
<div class="heading-container">
<div class="heading">
<input type="text" id="search" placeholder="Search...">
<div class="module">{{ head_name }}</div>
<div class="toggle-version-container">
<span>{{ version }}</span>
<div id="dark-mode-toggle" role="switch" aria-checked="false" aria-label="Toggle dark mode">{{ light_icon }}{{ dark_icon }}</div>
</div>
{{ menu_icon }}
</div>
</div>
<nav class="content hidden">
<ul>
{{ toc_links }}
</ul>
</nav>
</header>
<div class="doc-container">
<div class="doc-content">
{{ contents }}
<div class="footer">
{{ footer_content }}
</div>
</div>
{{ right_content }}
</div>
</div>
{{ footer_assets }}
</body>
</html>
'
)
enum OutputType {
@ -92,6 +137,12 @@ fn (mut cfg DocConfig) serve_html() {
if cfg.open_docs {
open_url(server_url)
}
content_type := match cfg.output_type {
.html { 'text/html' }
.markdown { 'text/markdown' }
.json { 'application/json' }
else { 'text/plain' }
}
for {
con := server.accept() or {
server.close() or { }
@ -106,7 +157,7 @@ fn (mut cfg DocConfig) serve_html() {
filename = if url.path == '/' { def_name } else { url.path.trim_left('/') }
}
html := docs[filename]
con.write('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n$html') or {
con.write('HTTP/1.1 200 OK\r\nContent-Type: $content_type\r\n\r\n$html') or {
con.close() or { return }
return
}
@ -127,9 +178,6 @@ fn get_src_link(repo_url string, file_name string, line_nr int) string {
'git.sir.ht' { '/tree/master/$file_name' }
else { '' }
}
if repo_url.starts_with('https://github.com/vlang/v') && !url.path.contains('master/vlib') {
url.path = url.path.replace('/blob/master/$file_name', '/blob/master/vlib/$file_name')
}
if url.path == '/' { return '' }
url.fragment = 'L$line_nr'
return url.str()
@ -138,12 +186,12 @@ fn get_src_link(repo_url string, file_name string, line_nr int) string {
fn js_compress(str string) string {
mut js := strings.new_builder(200)
lines := str.split_into_lines()
rules := [') {', ' = ', ', ', '{ ', ' }', ' (', '; ', ' + ', ' < ']
clean := ['){', '=', ',', '{', '}', '(', ';', '+', '<']
rules := [') {', ' = ', ', ', '{ ', ' }', ' (', '; ', ' + ', ' < ', ' - ', ' || ', ' var', ': ', ' >= ', ' && ', ' else if', ' === ', ' !== ', ' else ']
clean := ['){', '=', ',', '{', '}', '(', ';', '+', '<', '-', '||', 'var', ':', '>=', '&&', 'else if', '===', '!==', 'else']
for line in lines {
mut trimmed := line.trim_space()
if trimmed.starts_with('//') || (trimmed.starts_with('/*') && trimmed.ends_with('*/')) { continue }
for i, _ in rules {
for i in 0..rules.len-1 {
trimmed = trimmed.replace(rules[i], clean[i])
}
js.write(trimmed)
@ -256,13 +304,13 @@ fn doc_node_html(dd doc.DocNode, link string, head bool, tb &table.Table) string
hlighted_code := html_highlight(dd.content, tb)
is_const_class := if dd.name == 'Constants' { ' const' } else { '' }
mut sym_name := dd.name
if dd.parent_type !in ['void', '', 'Constants'] {
sym_name = '${dd.parent_type}.' + sym_name
if dd.attrs.exists('parent') && dd.attrs['parent'] !in ['void', '', 'Constants'] {
sym_name = dd.attrs['parent'] + '.' + sym_name
}
node_id := slug(sym_name)
hash_link := if !head { ' <a href="#$node_id">#</a>' } else { '' }
dnw.writeln('<section id="$node_id" class="doc-node$is_const_class">')
if dd.name != 'README' && dd.parent_type != 'Constants' {
if dd.name != 'README' && dd.attrs['parent'] != 'Constants' {
dnw.write('<div class="title"><$head_tag>$sym_name$hash_link</$head_tag>')
if link.len != 0 {
dnw.write('<a class="link" rel="noreferrer" target="_blank" href="$link">$link_svg</a>')
@ -279,10 +327,19 @@ fn doc_node_html(dd doc.DocNode, link string, head bool, tb &table.Table) string
return dnw.str()
}
fn (cfg DocConfig) readme_idx() int {
for i, dc in cfg.docs {
if dc.head.name != 'README' { continue }
return i
}
return -1
}
fn write_toc(cn doc.DocNode, nodes []doc.DocNode, toc &strings.Builder) {
toc.write('<li><a href="#${slug(cn.name)}">${cn.name}</a>')
toc_slug := if cn.content.len == 0 { '' } else { slug(cn.name) }
toc.write('<li class="open"><a href="#$toc_slug">${cn.name}</a>')
children := nodes.find_children_of(cn.name)
if children.len != 0 && cn.name != 'Constants' {
if cn.name != 'Constants' {
toc.writeln(' <ul>')
for child in children {
cname := cn.name + '.' + child.name
@ -295,34 +352,32 @@ fn write_toc(cn doc.DocNode, nodes []doc.DocNode, toc &strings.Builder) {
fn (cfg DocConfig) write_content(cn &doc.DocNode, dcs &doc.Doc, hw &strings.Builder) {
base_dir := os.base_dir(os.real_path(cfg.input_path))
file_path_name := cn.file_path.replace('$base_dir/', '')
file_path_name := if cfg.is_multi { cn.file_path.replace('$base_dir/', '') } else { os.file_name(cn.file_path) }
src_link := get_src_link(cfg.manifest.repo_url, file_path_name, cn.pos.line)
children := dcs.contents.find_children_of(cn.name)
if cn.content.len != 0 {
hw.write(doc_node_html(cn, src_link, false, dcs.table))
if children.len != 0 {
}
for child in children {
child_file_path_name := child.file_path.replace('$base_dir/', '')
child_src_link := get_src_link(cfg.manifest.repo_url, child_file_path_name, child.pos.line)
hw.write(doc_node_html(child, child_src_link, false, dcs.table))
}
}
}
fn (cfg DocConfig) gen_html(idx int) string {
dcs := cfg.docs[idx]
time_gen := '$dcs.time_generated.day $dcs.time_generated.smonth() $dcs.time_generated.year $dcs.time_generated.hhmmss()'
mut hw := strings.new_builder(200)
mut toc := strings.new_builder(200)
mut toc2 := strings.new_builder(200)
mut contents := strings.new_builder(200)
// generate toc first
const_node_idx := dcs.contents.index_by_name('Constants') or { -1 }
if const_node_idx != -1 {
write_toc(dcs.contents[const_node_idx], dcs.contents, &toc)
}
contents.writeln(doc_node_html(dcs.head, '', true, dcs.table))
for cn in dcs.contents {
if cn.name == 'Constants' || cn.parent_type !in ['void', ''] { continue }
cfg.write_content(&cn, &dcs, &contents)
if cn.attrs['parent'] == 'Constants' || cn.attrs['category'] == 'Methods' { continue }
write_toc(cn, dcs.contents, &toc)
} // write head
// get resources
doc_css := cfg.get_resource(css_js_assets[0], true)
normalize_css := cfg.get_resource(css_js_assets[1], true)
@ -331,47 +386,17 @@ fn (cfg DocConfig) gen_html(idx int) string {
dark_icon := cfg.get_resource('dark.svg', true)
menu_icon := cfg.get_resource('menu.svg', true)
arrow_icon := cfg.get_resource('arrow.svg', true)
hw.write('
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${dcs.head.name} | vdoc</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">')
// write css
if cfg.inline_assets {
hw.write('\n <style>$doc_css</style>')
hw.write('\n <style>$normalize_css</style>')
} else {
hw.write('\n <link rel="stylesheet" href="$doc_css" />')
hw.write('\n <link rel="stylesheet" href="$normalize_css" />')
}
version := if cfg.manifest.version.len != 0 { cfg.manifest.version } else { '' }
header_name := if cfg.is_multi && cfg.docs.len > 1 { os.file_name(os.real_path(cfg.input_path)) } else { dcs.head.name }
header_name := if cfg.is_multi && cfg.docs.len > 1 {
os.file_name(os.real_path(cfg.input_path))
} else if cfg.docs.len == 2 && idx+1 < cfg.docs.len && cfg.readme_idx() != -1 {
cfg.docs[cfg.readme_idx()+1].head.name
} else {
dcs.head.name
}
// write nav1
hw.write('
<body>
<div id="page">
<header class="doc-nav hidden">
<div class="heading-container">
<div class="heading">
<input type="text" id="search" placeholder="Search...">
<div class="module">${header_name}</div>
<div class="toggle-version-container">
<span>${version}</span>
<div id="dark-mode-toggle" role="switch" aria-checked="false" aria-label="Toggle dark mode">$light_icon $dark_icon</div>
</div>
$menu_icon
</div>
</div>
<nav class="content hidden">
<ul>')
if cfg.is_multi && cfg.docs.len > 1 {
if cfg.is_multi || cfg.docs.len > 1 {
mut submod_prefix := ''
mut docs := cfg.docs.filter(it.head.name == 'builtin')
docs << cfg.docs.filter(it.head.name != 'builtin')
@ -385,6 +410,8 @@ fn (cfg DocConfig) gen_html(idx int) string {
'./index.html'
} else if submod_prefix !in cfg.docs.map(it.head.name) {
'#'
} else if cfg.docs.len == 2 && cfg.readme_idx() == -1 {
'./docs.html'
} else {
'./' + doc.head.name + '.html'
}
@ -397,46 +424,44 @@ fn (cfg DocConfig) gen_html(idx int) string {
}
}
active_class := if doc.head.name == dcs.head.name { ' active' } else { '' }
hw.write('<li class="open$active_class"><div class="menu-row">$dropdown<a href="$href_name">${submod_prefix}</a></div>')
toc2.write('<li class="open$active_class"><div class="menu-row">$dropdown<a href="$href_name">${submod_prefix}</a></div>')
for j, cdoc in submodules {
if j == 0 {
hw.write('<ul>')
toc2.write('<ul>')
}
submod_name := cdoc.head.name.all_after(submod_prefix + '.')
sub_selected_classes := if cdoc.head.name == dcs.head.name { ' class="active"' } else { '' }
hw.write('<li$sub_selected_classes><a href="./${cdoc.head.name}.html">${submod_name}</a></li>')
toc2.write('<li$sub_selected_classes><a href="./${cdoc.head.name}.html">${submod_name}</a></li>')
if j == submodules.len - 1 {
hw.write('</ul>')
toc2.write('</ul>')
}
}
hw.write('</li>')
toc2.write('</li>')
}
}
return html_content
.replace('{{ title }}', dcs.head.name)
.replace('{{ head_name }}', header_name)
.replace('{{ version }}', version)
.replace('{{ light_icon }}', light_icon)
.replace('{{ dark_icon }}', dark_icon)
.replace('{{ menu_icon }}', menu_icon)
.replace('{{ head_assets }}', if cfg.inline_assets {
'\n <style>$doc_css</style>\n <style>$normalize_css</style>'
} else {
hw.writeln(toc.str())
}
hw.write('</ul>\n</nav>\n</header>')
hw.write('<div class="doc-container">\n<div class="doc-content">\n')
hw.write(doc_node_html(dcs.head, '', true, dcs.table))
if const_node_idx != -1 {
cfg.write_content(&dcs.contents[const_node_idx], &dcs, &hw)
}
for cn in dcs.contents {
if cn.parent_type !in ['void', ''] || cn.name == 'Constants' { continue }
cfg.write_content(&cn, &dcs, &hw)
}
hw.write('\n<div class="footer">Powered by vdoc. Generated on: $time_gen</div>\n</div>\n')
if cfg.is_multi && cfg.docs.len > 1 && dcs.head.name != 'README' {
hw.write('<div class="doc-toc">\n\n<ul>\n${toc.str()}</ul>\n</div>')
}
hw.write('</div></div>')
if cfg.inline_assets {
hw.write('<script>$doc_js</script>')
'\n <link rel="stylesheet" href="$doc_css" />\n <link rel="stylesheet" href="$normalize_css" />'
})
.replace('{{ toc_links }}', if cfg.is_multi || cfg.docs.len > 1 { toc2.str() } else { toc.str() })
.replace('{{ contents }}', contents.str())
.replace('{{ right_content }}', if cfg.is_multi && cfg.docs.len > 1 && dcs.head.name != 'README' {
'<div class="doc-toc"><ul>' + toc.str() + '</ul></div>'
} else { '' })
.replace('{{ footer_content }}', 'Powered by vdoc. Generated on: $time_gen')
.replace('{{ footer_assets }}', if cfg.inline_assets {
'<script>$doc_js</script>'
} else {
hw.write('<script src="$doc_js"></script>')
}
hw.write('</body>
</html>')
return hw.str()
'<script src="$doc_js"></script>'
})
}
fn (cfg DocConfig) gen_plaintext(idx int) string {
@ -482,13 +507,14 @@ fn (cfg DocConfig) gen_markdown(idx int, with_toc bool) string {
fn (cfg DocConfig) render() map[string]string {
mut docs := map[string]string
for i, doc in cfg.docs {
// since builtin is generated first, ignore it
mut name := if doc.head.name == 'README' {
mut name := if doc.head.name == 'README' || cfg.docs.len == 1 {
'index'
} else if !cfg.is_multi && !os.is_dir(cfg.output_path) {
os.file_name(cfg.output_path)
} else if i-1 >= 0 && cfg.readme_idx() != -1 && cfg.docs.len == 2 {
'docs'
} else {
doc.head.name
}
@ -579,7 +605,12 @@ fn (mut cfg DocConfig) generate_docs_from_file() {
mut dcs := doc.generate(dirpath, cfg.pub_only, true) or {
mut err_msg := err
if errcode == 1 {
err_msg += ' Use the `-m` flag if you are generating docs of a directory with multiple modules inside.'
mod_list := get_modules_list(cfg.input_path)
println('Available modules:\n==================')
for mod in mod_list {
println(mod.all_after('vlib/').all_after('modules/').replace('/', '.'))
}
err_msg += ' Use the `-m` flag if you are generating docs of a directory containing multiple modules.'
}
eprintln(err_msg)
exit(1)
@ -613,6 +644,11 @@ fn (mut cfg DocConfig) generate_docs_from_file() {
if !os.is_dir(cfg.output_path) {
cfg.output_path = os.real_path('.')
}
if !os.exists(cfg.output_path) {
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) {
@ -679,7 +715,7 @@ fn get_modules_list(path string) []string {
files := os.walk_ext(path, 'v')
mut dirs := []string{}
for file in files {
if 'test' in file || 'js' in file || 'x64' in file || 'bare' in file || 'uiold' in file || 'vweb' in file { continue }
if 'vlib' in path && ('examples' in file || 'test' in file || 'js' in file || 'x64' in file || 'bare' in file || 'uiold' in file || 'vweb' in file) { continue }
dirname := os.base_dir(file)
if dirname in dirs { continue }
dirs << dirname
@ -766,9 +802,11 @@ fn main() {
'-s' {
cfg.inline_assets = true
cfg.serve_http = true
if cfg.output_type == .unset {
cfg.output_type = .html
}
'-r' {
}
'-readme' {
cfg.include_readme = true
}
'-v' {
@ -784,6 +822,11 @@ fn main() {
eprintln('vdoc: No input path found.')
exit(1)
}
$if windows {
cfg.input_path = cfg.input_path.replace('/', os.path_separator)
} $else {
cfg.input_path = cfg.input_path.replace('\\', os.path_separator)
}
is_path := cfg.input_path.ends_with('.v') || cfg.input_path.split(os.path_separator).len > 1 || cfg.input_path == '.'
if cfg.input_path == 'vlib' {
cfg.is_multi = true

View File

@ -20,6 +20,6 @@ Options:
-open Launches the browser when the server docs has started.
-p Specifies the port to be used for the docs server.
-s Serve HTML-generated docs via HTTP.
-r Include README.md to docs if present.
-readme Include README.md to docs if present.
-v Enables verbose logging. For debugging purposes.
-h, -help Prints this help text.

View File

@ -123,7 +123,7 @@ pub fn (ftp FTP) login(user, passwd string) bool {
}
return false
}
mut code, mut data := ftp.read()
mut code, _ := ftp.read()
if code == logged_in {
return true
}

View File

@ -19,11 +19,11 @@ import picohttpparser
#include "src/picoev.h"
const (
MAX_FDS = 1024
TIMEOUT_SECS = 8
MAX_TIMEOUT = 10
MAX_READ = 4096
MAX_WRITE = 8192
max_fds = 1024
timeout_secs = 8
max_timeout = 10
max_read = 4096
max_write = 8192
)
struct C.in_addr {
@ -122,10 +122,10 @@ fn rw_callback(loop &C.picoev_loop, fd, events int, cb_arg voidptr) {
return
}
else if (events & C.PICOEV_READ) != 0 {
C.picoev_set_timeout(loop, fd, TIMEOUT_SECS)
buf := (p.buf + fd * MAX_READ)
C.picoev_set_timeout(loop, fd, timeout_secs)
buf := (p.buf + fd * max_read)
idx := p.idx[fd]
mut r := myread(fd, buf, MAX_READ, idx)
mut r := myread(fd, buf, max_read, idx)
if r == 0 {
close_conn(loop, fd)
p.idx[fd] = 0
@ -141,7 +141,7 @@ fn rw_callback(loop &C.picoev_loop, fd, events int, cb_arg voidptr) {
} else {
r += idx
mut s := tos(buf, r)
out := (p.out + fd * MAX_WRITE)
out := (p.out + fd * max_write)
mut res := picohttpparser.Response{
fd: fd
date: p.date
@ -191,7 +191,7 @@ fn accept_callback(loop &C.picoev_loop, fd, events int, cb_arg voidptr) {
newfd := C.accept(fd, 0, 0)
if newfd != -1 {
setup_sock(newfd)
C.picoev_add(loop, newfd, C.PICOEV_READ, TIMEOUT_SECS, rw_callback, cb_arg)
C.picoev_add(loop, newfd, C.PICOEV_READ, timeout_secs, rw_callback, cb_arg)
}
}
@ -223,14 +223,14 @@ pub fn new(port int, cb voidptr) &Picoev {
setup_sock(fd)
C.picoev_init(MAX_FDS)
loop := C.picoev_create_loop(MAX_TIMEOUT)
C.picoev_init(max_fds)
loop := C.picoev_create_loop(max_timeout)
pv := &Picoev{
loop: loop
cb: cb
date: C.get_date()
buf: malloc(MAX_FDS * MAX_READ + 1)
out: malloc(MAX_FDS * MAX_WRITE + 1)
buf: malloc(max_fds * max_read + 1)
out: malloc(max_fds * max_write + 1)
}
C.picoev_add(loop, fd, C.PICOEV_READ, 0, accept_callback, pv)

View File

@ -21,6 +21,7 @@ pub mut:
head DocNode
with_comments bool = true
contents []DocNode
fmt fmt.Fmt
time_generated time.Time
}
@ -37,7 +38,7 @@ pub mut:
comment string
pos DocPos = DocPos{-1, -1}
file_path string = ''
parent_type string = ''
attrs map[string]string
}
pub fn merge_comments(stmts []ast.Stmt) string {
@ -112,65 +113,55 @@ fn convert_pos(file_path string, pos token.Position) DocPos {
}
}
pub fn (d Doc) get_signature(stmt ast.Stmt, file &ast.File) string {
mut f := fmt.Fmt{
out: strings.new_builder(1000)
out_imports: strings.new_builder(200)
table: d.table
file: file
cur_mod: d.head.name.split('.').last()
indent: 0
is_debug: false
}
f.process_file_imports(file)
pub fn (mut d Doc) get_signature(stmt ast.Stmt, file &ast.File) string {
match stmt {
ast.Module {
return 'module $it.name'
return 'module $stmt.name'
}
ast.FnDecl {
return it.str(d.table).replace(f.cur_mod + '.', '')
return stmt.str(d.table).replace(d.fmt.cur_mod + '.', '')
}
else {
f.stmt(stmt)
return f.out.str().trim_space()
d.fmt.out = strings.new_builder(1000)
d.fmt.stmt(stmt)
return d.fmt.out.str().trim_space()
}
}
}
pub fn (d Doc) get_pos(stmt ast.Stmt) token.Position {
match stmt {
ast.FnDecl { return it.pos }
ast.StructDecl { return it.pos }
ast.EnumDecl { return it.pos }
ast.InterfaceDecl { return it.pos }
ast.ConstDecl { return it.pos }
ast.FnDecl { return stmt.pos }
ast.StructDecl { return stmt.pos }
ast.EnumDecl { return stmt.pos }
ast.InterfaceDecl { return stmt.pos }
ast.ConstDecl { return stmt.pos }
else { return token.Position{} }
}
}
pub fn (d Doc) get_type_name(decl ast.TypeDecl) string {
match decl {
ast.SumTypeDecl { return it.name }
ast.FnTypeDecl { return it.name }
ast.AliasTypeDecl { return it.name }
ast.SumTypeDecl { return decl.name }
ast.FnTypeDecl { return decl.name }
ast.AliasTypeDecl { return decl.name }
}
}
pub fn (d Doc) get_name(stmt ast.Stmt) string {
cur_mod := d.head.name.split('.').last()
match stmt {
ast.FnDecl { return it.name }
ast.StructDecl { return it.name }
ast.EnumDecl { return it.name }
ast.InterfaceDecl { return it.name }
ast.TypeDecl { return d.get_type_name(it).replace('&' + cur_mod + '.', '').replace(cur_mod + '.', '') }
ast.FnDecl { return stmt.name }
ast.StructDecl { return stmt.name }
ast.EnumDecl { return stmt.name }
ast.InterfaceDecl { return stmt.name }
ast.TypeDecl { return d.get_type_name(stmt) }
ast.ConstDecl { return 'Constants' }
else { return '' }
}
}
pub fn new(input_path string) Doc {
return Doc{
mut d := Doc{
input_path: os.real_path(input_path)
prefs: &pref.Preferences{}
table: table.new_table()
@ -178,28 +169,59 @@ pub fn new(input_path string) Doc {
contents: []DocNode{}
time_generated: time.now()
}
d.fmt = fmt.Fmt{
indent: 0
is_debug: false
table: d.table
}
return d
}
pub fn (nodes []DocNode) index_by_name(node_name string) ?int {
pub fn (mut nodes []DocNode) sort_by_name() {
nodes.sort_with_compare(compare_nodes_by_name)
}
pub fn (mut nodes []DocNode) sort_by_category() {
nodes.sort_with_compare(compare_nodes_by_category)
}
fn compare_nodes_by_name(a, b &DocNode) int {
al := a.name.to_lower()
bl := b.name.to_lower()
return compare_strings(al, bl)
}
fn compare_nodes_by_category(a, b &DocNode) int {
al := a.attrs['category']
bl := b.attrs['category']
return compare_strings(al, bl)
}
pub fn (nodes []DocNode) index_by_name(node_name string) int {
for i, node in nodes {
if node.name != node_name { continue }
return i
}
return error('Node with the name "$node_name" was not found.')
return -1
}
pub fn (nodes []DocNode) find_children_of(parent_type string) []DocNode {
if parent_type.len == 0 {
return []DocNode{}
pub fn (nodes []DocNode) find_children_of(parent string) []DocNode {
return nodes.find_nodes_with_attr('parent', parent)
}
pub fn (nodes []DocNode) find_nodes_with_attr(attr_name string, value string) []DocNode {
mut subgroup := []DocNode{}
if attr_name.len == 0 {
return subgroup
}
mut children := []DocNode{}
for node in nodes {
if node.parent_type != parent_type {
if !node.attrs.exists(attr_name) || node.attrs[attr_name] != value {
continue
}
children << node
subgroup << node
}
return children
subgroup.sort_by_name()
return subgroup
}
fn get_parent_mod(dir string) ?string {
@ -242,7 +264,7 @@ fn get_parent_mod(dir string) ?string {
return file_ast.mod.name
}
pub fn (mut d Doc) generate() ?bool {
fn (mut d Doc) generate() ?Doc {
// get all files
base_path := if os.is_dir(d.input_path) { d.input_path } else { os.real_path(os.base_dir(d.input_path)) }
project_files := os.ls(base_path) or {
@ -285,7 +307,9 @@ pub fn (mut d Doc) generate() ?bool {
continue
}
stmts := file_ast.stmts
//
d.fmt.file = file_ast
d.fmt.cur_mod = orig_mod_name
d.fmt.process_file_imports(file_ast)
mut last_import_stmt_idx := 0
for sidx, stmt in stmts {
if stmt is ast.Import {
@ -306,10 +330,7 @@ pub fn (mut d Doc) generate() ?bool {
module_comment := get_comment_block_right_before(prev_comments)
prev_comments = []
if 'vlib' !in base_path && !module_comment.starts_with('Copyright (c)') {
if module_comment == '' {
continue
}
if module_comment == d.head.comment {
if module_comment in ['', d.head.comment] {
continue
}
if d.head.comment != '' {
@ -350,25 +371,45 @@ pub fn (mut d Doc) generate() ?bool {
pos: convert_pos(v_files[i], pos)
file_path: v_files[i]
}
if node.name.len == 0 && node.comment.len == 0 && node.content.len == 0 {
continue
}
if stmt is ast.FnDecl {
fnd := stmt as ast.FnDecl
if fnd.receiver.typ != 0 {
mut parent_type := d.table.get_type_name(fnd.receiver.typ)
if parent_type.starts_with(module_name + '.') {
parent_type = parent_type.all_after(module_name + '.')
node.attrs['parent'] = d.fmt.type_to_str(fnd.receiver.typ).trim_left('&')
p_idx := d.contents.index_by_name(node.attrs['parent'])
if p_idx == -1 && node.attrs['parent'] != 'void' {
d.contents << DocNode{
name: node.attrs['parent']
content: ''
comment: ''
attrs: {'category': 'Structs'}
}
}
node.parent_type = parent_type
}
}
if stmt is ast.ConstDecl {
if const_idx == -1 {
const_idx = sidx
} else {
node.parent_type = 'Constants'
node.attrs['parent'] = 'Constants'
}
}
if node.name.len == 0 && node.comment.len == 0 && node.content.len == 0 {
continue
match stmt {
ast.ConstDecl { node.attrs['category'] = 'Constants' }
ast.EnumDecl { node.attrs['category'] = 'Enums' }
ast.InterfaceDecl { node.attrs['category'] = 'Interfaces' }
ast.StructDecl { node.attrs['category'] = 'Structs' }
ast.TypeDecl { node.attrs['category'] = 'Typedefs' }
ast.FnDecl {
node.attrs['category'] = if node.attrs['parent'] in ['void', ''] || !node.attrs.exists('parent') {
'Functions'
} else {
'Methods'
}
}
else {}
}
d.contents << node
if d.with_comments && (prev_comments.len > 0) {
@ -378,17 +419,18 @@ pub fn (mut d Doc) generate() ?bool {
}
prev_comments = []
}
d.fmt.mod2alias = map[string]string{}
}
d.time_generated = time.now()
return true
d.contents.sort_by_name()
d.contents.sort_by_category()
return d
}
pub fn generate(input_path string, pub_only, with_comments bool) ?Doc {
mut doc := new(input_path)
doc.pub_only = pub_only
doc.with_comments = with_comments
doc.generate() or {
return error_with_code(err, errcode)
}
return doc
return doc.generate()
}

View File

@ -514,7 +514,7 @@ pub fn (mut f Fmt) struct_field_expr(fexpr ast.Expr) {
}
}
fn (f &Fmt) type_to_str(t table.Type) string {
pub fn (f &Fmt) type_to_str(t table.Type) string {
mut res := f.table.type_to_str(t)
for res.ends_with('_ptr') {
// type_ptr => &type

View File

@ -282,6 +282,7 @@ fn os_from_string(os string) pref.OS {
// Helper function to convert string names to CC enum
pub fn cc_from_string(cc_str string) pref.CompilerType {
if cc_str.len == 0 { return .gcc }
cc := cc_str.replace('\\', '/').split('/').last().all_before('.')
if 'tcc' in cc { return .tinyc }
if 'tinyc' in cc { return .tinyc }