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

View File

@ -36,6 +36,51 @@ const (
exe_dir = os.dir(exe_path) exe_dir = os.dir(exe_path)
res_path = os.join_path(exe_dir, 'vdoc-resources') res_path = os.join_path(exe_dir, 'vdoc-resources')
vexe_path = os.base_dir(@VEXE) 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 { enum OutputType {
@ -92,6 +137,12 @@ fn (mut cfg DocConfig) serve_html() {
if cfg.open_docs { if cfg.open_docs {
open_url(server_url) open_url(server_url)
} }
content_type := match cfg.output_type {
.html { 'text/html' }
.markdown { 'text/markdown' }
.json { 'application/json' }
else { 'text/plain' }
}
for { for {
con := server.accept() or { con := server.accept() or {
server.close() 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('/') } filename = if url.path == '/' { def_name } else { url.path.trim_left('/') }
} }
html := docs[filename] 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 } con.close() or { return }
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' } 'git.sir.ht' { '/tree/master/$file_name' }
else { '' } 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 '' } if url.path == '/' { return '' }
url.fragment = 'L$line_nr' url.fragment = 'L$line_nr'
return url.str() 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 { fn js_compress(str string) string {
mut js := strings.new_builder(200) mut js := strings.new_builder(200)
lines := str.split_into_lines() lines := str.split_into_lines()
rules := [') {', ' = ', ', ', '{ ', ' }', ' (', '; ', ' + ', ' < '] rules := [') {', ' = ', ', ', '{ ', ' }', ' (', '; ', ' + ', ' < ', ' - ', ' || ', ' var', ': ', ' >= ', ' && ', ' else if', ' === ', ' !== ', ' else ']
clean := ['){', '=', ',', '{', '}', '(', ';', '+', '<'] clean := ['){', '=', ',', '{', '}', '(', ';', '+', '<', '-', '||', 'var', ':', '>=', '&&', 'else if', '===', '!==', 'else']
for line in lines { for line in lines {
mut trimmed := line.trim_space() mut trimmed := line.trim_space()
if trimmed.starts_with('//') || (trimmed.starts_with('/*') && trimmed.ends_with('*/')) { continue } 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]) trimmed = trimmed.replace(rules[i], clean[i])
} }
js.write(trimmed) 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) hlighted_code := html_highlight(dd.content, tb)
is_const_class := if dd.name == 'Constants' { ' const' } else { '' } is_const_class := if dd.name == 'Constants' { ' const' } else { '' }
mut sym_name := dd.name mut sym_name := dd.name
if dd.parent_type !in ['void', '', 'Constants'] { if dd.attrs.exists('parent') && dd.attrs['parent'] !in ['void', '', 'Constants'] {
sym_name = '${dd.parent_type}.' + sym_name sym_name = dd.attrs['parent'] + '.' + sym_name
} }
node_id := slug(sym_name) node_id := slug(sym_name)
hash_link := if !head { ' <a href="#$node_id">#</a>' } else { '' } hash_link := if !head { ' <a href="#$node_id">#</a>' } else { '' }
dnw.writeln('<section id="$node_id" class="doc-node$is_const_class">') 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>') dnw.write('<div class="title"><$head_tag>$sym_name$hash_link</$head_tag>')
if link.len != 0 { if link.len != 0 {
dnw.write('<a class="link" rel="noreferrer" target="_blank" href="$link">$link_svg</a>') 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() 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) { 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) children := nodes.find_children_of(cn.name)
if children.len != 0 && cn.name != 'Constants' { if cn.name != 'Constants' {
toc.writeln(' <ul>') toc.writeln(' <ul>')
for child in children { for child in children {
cname := cn.name + '.' + child.name 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) { 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)) 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) src_link := get_src_link(cfg.manifest.repo_url, file_path_name, cn.pos.line)
children := dcs.contents.find_children_of(cn.name) children := dcs.contents.find_children_of(cn.name)
hw.write(doc_node_html(cn, src_link, false, dcs.table)) if cn.content.len != 0 {
if children.len != 0 { hw.write(doc_node_html(cn, src_link, false, dcs.table))
for child in children { }
child_file_path_name := child.file_path.replace('$base_dir/', '') for child in children {
child_src_link := get_src_link(cfg.manifest.repo_url, child_file_path_name, child.pos.line) child_file_path_name := child.file_path.replace('$base_dir/', '')
hw.write(doc_node_html(child, child_src_link, false, dcs.table)) 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 { fn (cfg DocConfig) gen_html(idx int) string {
dcs := cfg.docs[idx] dcs := cfg.docs[idx]
time_gen := '$dcs.time_generated.day $dcs.time_generated.smonth() $dcs.time_generated.year $dcs.time_generated.hhmmss()' 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 toc := strings.new_builder(200)
mut toc2 := strings.new_builder(200)
mut contents := strings.new_builder(200)
// generate toc first // generate toc first
const_node_idx := dcs.contents.index_by_name('Constants') or { -1 } contents.writeln(doc_node_html(dcs.head, '', true, dcs.table))
if const_node_idx != -1 {
write_toc(dcs.contents[const_node_idx], dcs.contents, &toc)
}
for cn in dcs.contents { 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_toc(cn, dcs.contents, &toc)
} // write head } // write head
// get resources // get resources
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) 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) dark_icon := cfg.get_resource('dark.svg', true)
menu_icon := cfg.get_resource('menu.svg', true) menu_icon := cfg.get_resource('menu.svg', true)
arrow_icon := cfg.get_resource('arrow.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 // 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 { '' } 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 // write nav1
hw.write(' if cfg.is_multi || cfg.docs.len > 1 {
<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 {
mut submod_prefix := '' mut submod_prefix := ''
mut docs := cfg.docs.filter(it.head.name == 'builtin') mut docs := cfg.docs.filter(it.head.name == 'builtin')
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' './index.html'
} else if submod_prefix !in cfg.docs.map(it.head.name) { } else if submod_prefix !in cfg.docs.map(it.head.name) {
'#' '#'
} else if cfg.docs.len == 2 && cfg.readme_idx() == -1 {
'./docs.html'
} else { } else {
'./' + doc.head.name + '.html' './' + 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 { '' } 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 { for j, cdoc in submodules {
if j == 0 { if j == 0 {
hw.write('<ul>') toc2.write('<ul>')
} }
submod_name := cdoc.head.name.all_after(submod_prefix + '.') submod_name := cdoc.head.name.all_after(submod_prefix + '.')
sub_selected_classes := if cdoc.head.name == dcs.head.name { ' class="active"' } else { '' } 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 { if j == submodules.len - 1 {
hw.write('</ul>') toc2.write('</ul>')
} }
} }
hw.write('</li>') toc2.write('</li>')
} }
} else {
hw.writeln(toc.str())
} }
hw.write('</ul>\n</nav>\n</header>') return html_content
hw.write('<div class="doc-container">\n<div class="doc-content">\n') .replace('{{ title }}', dcs.head.name)
hw.write(doc_node_html(dcs.head, '', true, dcs.table)) .replace('{{ head_name }}', header_name)
if const_node_idx != -1 { .replace('{{ version }}', version)
cfg.write_content(&dcs.contents[const_node_idx], &dcs, &hw) .replace('{{ light_icon }}', light_icon)
} .replace('{{ dark_icon }}', dark_icon)
for cn in dcs.contents { .replace('{{ menu_icon }}', menu_icon)
if cn.parent_type !in ['void', ''] || cn.name == 'Constants' { continue } .replace('{{ head_assets }}', if cfg.inline_assets {
cfg.write_content(&cn, &dcs, &hw) '\n <style>$doc_css</style>\n <style>$normalize_css</style>'
} } else {
hw.write('\n<div class="footer">Powered by vdoc. Generated on: $time_gen</div>\n</div>\n') '\n <link rel="stylesheet" href="$doc_css" />\n <link rel="stylesheet" href="$normalize_css" />'
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>') .replace('{{ toc_links }}', if cfg.is_multi || cfg.docs.len > 1 { toc2.str() } else { toc.str() })
} .replace('{{ contents }}', contents.str())
hw.write('</div></div>') .replace('{{ right_content }}', if cfg.is_multi && cfg.docs.len > 1 && dcs.head.name != 'README' {
if cfg.inline_assets { '<div class="doc-toc"><ul>' + toc.str() + '</ul></div>'
hw.write('<script>$doc_js</script>') } else { '' })
} else { .replace('{{ footer_content }}', 'Powered by vdoc. Generated on: $time_gen')
hw.write('<script src="$doc_js"></script>') .replace('{{ footer_assets }}', if cfg.inline_assets {
} '<script>$doc_js</script>'
hw.write('</body> } else {
</html>') '<script src="$doc_js"></script>'
return hw.str() })
} }
fn (cfg DocConfig) gen_plaintext(idx int) string { 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 { fn (cfg DocConfig) render() map[string]string {
mut docs := map[string]string mut docs := map[string]string
for i, doc in cfg.docs { for i, doc in cfg.docs {
// since builtin is generated first, ignore it // 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' 'index'
} else if !cfg.is_multi && !os.is_dir(cfg.output_path) { } else if !cfg.is_multi && !os.is_dir(cfg.output_path) {
os.file_name(cfg.output_path) os.file_name(cfg.output_path)
} else if i-1 >= 0 && cfg.readme_idx() != -1 && cfg.docs.len == 2 {
'docs'
} else { } else {
doc.head.name 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 dcs := doc.generate(dirpath, cfg.pub_only, true) or {
mut err_msg := err mut err_msg := err
if errcode == 1 { 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) eprintln(err_msg)
exit(1) exit(1)
@ -613,6 +644,11 @@ fn (mut cfg DocConfig) generate_docs_from_file() {
if !os.is_dir(cfg.output_path) { if !os.is_dir(cfg.output_path) {
cfg.output_path = os.real_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 { if cfg.is_multi {
cfg.output_path = os.join_path(cfg.output_path, '_docs') cfg.output_path = os.join_path(cfg.output_path, '_docs')
if !os.exists(cfg.output_path) { if !os.exists(cfg.output_path) {
@ -679,7 +715,7 @@ fn get_modules_list(path string) []string {
files := os.walk_ext(path, 'v') files := os.walk_ext(path, 'v')
mut dirs := []string{} mut dirs := []string{}
for file in files { 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) dirname := os.base_dir(file)
if dirname in dirs { continue } if dirname in dirs { continue }
dirs << dirname dirs << dirname
@ -766,9 +802,11 @@ fn main() {
'-s' { '-s' {
cfg.inline_assets = true cfg.inline_assets = true
cfg.serve_http = true cfg.serve_http = true
cfg.output_type = .html if cfg.output_type == .unset {
cfg.output_type = .html
}
} }
'-r' { '-readme' {
cfg.include_readme = true cfg.include_readme = true
} }
'-v' { '-v' {
@ -784,6 +822,11 @@ fn main() {
eprintln('vdoc: No input path found.') eprintln('vdoc: No input path found.')
exit(1) 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 == '.' 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' { if cfg.input_path == 'vlib' {
cfg.is_multi = true cfg.is_multi = true

View File

@ -20,6 +20,6 @@ Options:
-open Launches the browser when the server docs has started. -open Launches the browser when the server docs has started.
-p Specifies the port to be used for the docs server. -p Specifies the port to be used for the docs server.
-s Serve HTML-generated docs via HTTP. -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. -v Enables verbose logging. For debugging purposes.
-h, -help Prints this help text. -h, -help Prints this help text.

View File

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

View File

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

View File

@ -21,6 +21,7 @@ pub mut:
head DocNode head DocNode
with_comments bool = true with_comments bool = true
contents []DocNode contents []DocNode
fmt fmt.Fmt
time_generated time.Time time_generated time.Time
} }
@ -37,7 +38,7 @@ pub mut:
comment string comment string
pos DocPos = DocPos{-1, -1} pos DocPos = DocPos{-1, -1}
file_path string = '' file_path string = ''
parent_type string = '' attrs map[string]string
} }
pub fn merge_comments(stmts []ast.Stmt) 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 { pub fn (mut 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)
match stmt { match stmt {
ast.Module { ast.Module {
return 'module $it.name' return 'module $stmt.name'
} }
ast.FnDecl { ast.FnDecl {
return it.str(d.table).replace(f.cur_mod + '.', '') return stmt.str(d.table).replace(d.fmt.cur_mod + '.', '')
} }
else { else {
f.stmt(stmt) d.fmt.out = strings.new_builder(1000)
return f.out.str().trim_space() d.fmt.stmt(stmt)
return d.fmt.out.str().trim_space()
} }
} }
} }
pub fn (d Doc) get_pos(stmt ast.Stmt) token.Position { pub fn (d Doc) get_pos(stmt ast.Stmt) token.Position {
match stmt { match stmt {
ast.FnDecl { return it.pos } ast.FnDecl { return stmt.pos }
ast.StructDecl { return it.pos } ast.StructDecl { return stmt.pos }
ast.EnumDecl { return it.pos } ast.EnumDecl { return stmt.pos }
ast.InterfaceDecl { return it.pos } ast.InterfaceDecl { return stmt.pos }
ast.ConstDecl { return it.pos } ast.ConstDecl { return stmt.pos }
else { return token.Position{} } else { return token.Position{} }
} }
} }
pub fn (d Doc) get_type_name(decl ast.TypeDecl) string { pub fn (d Doc) get_type_name(decl ast.TypeDecl) string {
match decl { match decl {
ast.SumTypeDecl { return it.name } ast.SumTypeDecl { return decl.name }
ast.FnTypeDecl { return it.name } ast.FnTypeDecl { return decl.name }
ast.AliasTypeDecl { return it.name } ast.AliasTypeDecl { return decl.name }
} }
} }
pub fn (d Doc) get_name(stmt ast.Stmt) string { pub fn (d Doc) get_name(stmt ast.Stmt) string {
cur_mod := d.head.name.split('.').last()
match stmt { match stmt {
ast.FnDecl { return it.name } ast.FnDecl { return stmt.name }
ast.StructDecl { return it.name } ast.StructDecl { return stmt.name }
ast.EnumDecl { return it.name } ast.EnumDecl { return stmt.name }
ast.InterfaceDecl { return it.name } ast.InterfaceDecl { return stmt.name }
ast.TypeDecl { return d.get_type_name(it).replace('&' + cur_mod + '.', '').replace(cur_mod + '.', '') } ast.TypeDecl { return d.get_type_name(stmt) }
ast.ConstDecl { return 'Constants' } ast.ConstDecl { return 'Constants' }
else { return '' } else { return '' }
} }
} }
pub fn new(input_path string) Doc { pub fn new(input_path string) Doc {
return Doc{ mut d := Doc{
input_path: os.real_path(input_path) input_path: os.real_path(input_path)
prefs: &pref.Preferences{} prefs: &pref.Preferences{}
table: table.new_table() table: table.new_table()
@ -178,28 +169,59 @@ pub fn new(input_path string) Doc {
contents: []DocNode{} contents: []DocNode{}
time_generated: time.now() 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 { for i, node in nodes {
if node.name != node_name { continue } if node.name != node_name { continue }
return i 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 { pub fn (nodes []DocNode) find_children_of(parent string) []DocNode {
if parent_type.len == 0 { return nodes.find_nodes_with_attr('parent', parent)
return []DocNode{} }
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 { for node in nodes {
if node.parent_type != parent_type { if !node.attrs.exists(attr_name) || node.attrs[attr_name] != value {
continue continue
} }
children << node subgroup << node
} }
return children subgroup.sort_by_name()
return subgroup
} }
fn get_parent_mod(dir string) ?string { fn get_parent_mod(dir string) ?string {
@ -242,7 +264,7 @@ fn get_parent_mod(dir string) ?string {
return file_ast.mod.name return file_ast.mod.name
} }
pub fn (mut d Doc) generate() ?bool { fn (mut d Doc) generate() ?Doc {
// get all files // 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)) } 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 { project_files := os.ls(base_path) or {
@ -285,7 +307,9 @@ pub fn (mut d Doc) generate() ?bool {
continue continue
} }
stmts := file_ast.stmts 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 mut last_import_stmt_idx := 0
for sidx, stmt in stmts { for sidx, stmt in stmts {
if stmt is ast.Import { 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) module_comment := get_comment_block_right_before(prev_comments)
prev_comments = [] prev_comments = []
if 'vlib' !in base_path && !module_comment.starts_with('Copyright (c)') { if 'vlib' !in base_path && !module_comment.starts_with('Copyright (c)') {
if module_comment == '' { if module_comment in ['', d.head.comment] {
continue
}
if module_comment == d.head.comment {
continue continue
} }
if d.head.comment != '' { if d.head.comment != '' {
@ -350,25 +371,45 @@ pub fn (mut d Doc) generate() ?bool {
pos: convert_pos(v_files[i], pos) pos: convert_pos(v_files[i], pos)
file_path: v_files[i] file_path: v_files[i]
} }
if node.name.len == 0 && node.comment.len == 0 && node.content.len == 0 {
continue
}
if stmt is ast.FnDecl { if stmt is ast.FnDecl {
fnd := stmt as ast.FnDecl fnd := stmt as ast.FnDecl
if fnd.receiver.typ != 0 { if fnd.receiver.typ != 0 {
mut parent_type := d.table.get_type_name(fnd.receiver.typ) node.attrs['parent'] = d.fmt.type_to_str(fnd.receiver.typ).trim_left('&')
if parent_type.starts_with(module_name + '.') { p_idx := d.contents.index_by_name(node.attrs['parent'])
parent_type = parent_type.all_after(module_name + '.') 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 stmt is ast.ConstDecl {
if const_idx == -1 { if const_idx == -1 {
const_idx = sidx const_idx = sidx
} else { } else {
node.parent_type = 'Constants' node.attrs['parent'] = 'Constants'
} }
} }
if node.name.len == 0 && node.comment.len == 0 && node.content.len == 0 { match stmt {
continue 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 d.contents << node
if d.with_comments && (prev_comments.len > 0) { if d.with_comments && (prev_comments.len > 0) {
@ -378,17 +419,18 @@ pub fn (mut d Doc) generate() ?bool {
} }
prev_comments = [] prev_comments = []
} }
d.fmt.mod2alias = map[string]string{}
} }
d.time_generated = time.now() 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 { pub fn generate(input_path string, pub_only, with_comments bool) ?Doc {
mut doc := new(input_path) mut doc := new(input_path)
doc.pub_only = pub_only doc.pub_only = pub_only
doc.with_comments = with_comments doc.with_comments = with_comments
doc.generate() or { return doc.generate()
return error_with_code(err, errcode)
}
return doc
} }

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) mut res := f.table.type_to_str(t)
for res.ends_with('_ptr') { for res.ends_with('_ptr') {
// type_ptr => &type // 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 // Helper function to convert string names to CC enum
pub fn cc_from_string(cc_str string) pref.CompilerType { 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('.') cc := cc_str.replace('\\', '/').split('/').last().all_before('.')
if 'tcc' in cc { return .tinyc } if 'tcc' in cc { return .tinyc }
if 'tinyc' in cc { return .tinyc } if 'tinyc' in cc { return .tinyc }