vdoc: fix syntax highlighting for symbols before ! and between ( and , (#19888)

This commit is contained in:
Swastik Baranwal 2023-11-16 23:44:28 +05:30 committed by GitHub
parent c9429f3331
commit 4458e49652
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 453 additions and 88 deletions

View File

@ -185,3 +185,11 @@ tests in the main V repository, for example:
* `v vet vlib/v` - run a style checker. * `v vet vlib/v` - run a style checker.
* `v test-self` (run self tests) in various compilation modes. * `v test-self` (run self tests) in various compilation modes.
> **Note**
The VDOC test vdoc_file_test.v now also supports VAUTOFIX, which is
useful, if you change anything inside cmd/tools/vdoc or vlib/v/doc/,
or inside the modules that it depends on (like markdown).
After such changes, just run this command *2 times*, and commit the
resulting changes in `cmd/tools/vdoc/tests/testdata` as well:
`VAUTOFIX=1 ./v cmd/tools/vdoc/tests/vdoc_file_test.v`

3
cmd/tools/vdoc/.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
*.ansi text eol=lf
*.text text eol=lf
*.html text eol=lf

View File

@ -198,11 +198,14 @@ fn (vd VDoc) gen_html(d doc.Doc) string {
} }
for cn in dcs_contents { for cn in dcs_contents {
vd.write_content(&cn, &d, mut contents) vd.write_content(&cn, &d, mut contents)
write_toc(cn, mut symbols_toc) write_toc(cn, mut symbols_toc) // write head
} // write head }
if cfg.html_only_contents {
// no need for theming, styling etc, useful for testing and for external documentation generators
return contents.str()
}
// write css // write css
mut version := if vd.manifest.version.len != 0 { vd.manifest.version } else { '' }
version = [version, @VCURRENTHASH].join(' ')
header_name := if cfg.is_multi && vd.docs.len > 1 { header_name := if cfg.is_multi && vd.docs.len > 1 {
os.file_name(os.real_path(cfg.input_path)) os.file_name(os.real_path(cfg.input_path))
} else { } else {
@ -249,33 +252,65 @@ fn (vd VDoc) gen_html(d doc.Doc) string {
} }
modules_toc_str := modules_toc.str() modules_toc_str := modules_toc.str()
symbols_toc_str := symbols_toc.str() symbols_toc_str := symbols_toc.str()
result := (os.read_file(os.join_path(cfg.theme_dir, 'index.html')) or { panic(err) }).replace('{{ title }}', mut result := os.read_file(os.join_path(cfg.theme_dir, 'index.html')) or { panic(err) }
d.head.name).replace('{{ head_name }}', header_name).replace('{{ version }}', if cfg.html_no_vhash {
version).replace('{{ light_icon }}', vd.assets['light_icon']).replace('{{ dark_icon }}', result = result.replace('{{ version }}', 'latest')
vd.assets['dark_icon']).replace('{{ menu_icon }}', vd.assets['menu_icon']).replace('{{ head_assets }}', } else {
if cfg.inline_assets { mut version := if vd.manifest.version.len != 0 { vd.manifest.version } else { '' }
'<style>${vd.assets['doc_css']}</style> version = [version, @VCURRENTHASH].join(' ')
result = result.replace('{{ version }}', version)
}
result = result.replace('{{ title }}', d.head.name)
result = result.replace('{{ head_name }}', header_name)
result = result.replace('{{ light_icon }}', vd.assets['light_icon'])
result = result.replace('{{ dark_icon }}', vd.assets['dark_icon'])
result = result.replace('{{ menu_icon }}', vd.assets['menu_icon'])
if cfg.html_no_assets {
result = result.replace('{{ head_assets }}', '')
} else {
result = result.replace('{{ head_assets }}', if cfg.inline_assets {
'<style>${vd.assets['doc_css']}</style>
${tabs(2)}<style>${vd.assets['normalize_css']}</style> ${tabs(2)}<style>${vd.assets['normalize_css']}</style>
${tabs(2)}<script>${vd.assets['dark_mode_js']}</script>' ${tabs(2)}<script>${vd.assets['dark_mode_js']}</script>'
} else { } else {
'<link rel="stylesheet" href="${vd.assets['doc_css']}" /> '<link rel="stylesheet" href="${vd.assets['doc_css']}" />
${tabs(2)}<link rel="stylesheet" href="${vd.assets['normalize_css']}" /> ${tabs(2)}<link rel="stylesheet" href="${vd.assets['normalize_css']}" />
${tabs(2)}<script src="${vd.assets['dark_mode_js']}"></script>' ${tabs(2)}<script src="${vd.assets['dark_mode_js']}"></script>'
}).replace('{{ toc_links }}', if cfg.is_multi || vd.docs.len > 1 { })
modules_toc_str }
if cfg.html_no_toc_urls {
result = result.replace('{{ toc_links }}', '')
} else { } else {
symbols_toc_str result = result.replace('{{ toc_links }}', if cfg.is_multi || vd.docs.len > 1 {
}).replace('{{ contents }}', contents.str()).replace('{{ right_content }}', if cfg.is_multi modules_toc_str
&& d.head.name != 'README' { } else {
'<div class="doc-toc"><ul>${symbols_toc_str}</ul></div>' symbols_toc_str
})
}
result = result.replace('{{ contents }}', contents.str())
if cfg.html_no_right {
result = result.replace('{{ right_content }}', '')
} else { } else {
'' result = result.replace('{{ right_content }}', if cfg.is_multi && d.head.name != 'README' {
}).replace('{{ footer_content }}', gen_footer_text(d, !cfg.no_timestamp)).replace('{{ footer_assets }}', '<div class="doc-toc"><ul>${symbols_toc_str}</ul></div>'
if cfg.inline_assets { } else {
'<script>${vd.assets['doc_js']}</script>' ''
})
}
if cfg.html_no_footer {
result = result.replace('{{ footer_content }}', '')
} else { } else {
'<script src="${vd.assets['doc_js']}"></script>' result = result.replace('{{ footer_content }}', gen_footer_text(d, !cfg.no_timestamp))
}) }
if cfg.html_no_assets {
result = result.replace('{{ footer_assets }}', '')
} else {
result = result.replace('{{ footer_assets }}', if cfg.inline_assets {
'<script>${vd.assets['doc_js']}</script>'
} else {
'<script src="${vd.assets['doc_js']}"></script>'
})
}
return result return result
} }

View File

@ -10,7 +10,36 @@ const vexe = os.getenv_opt('VEXE') or { @VEXE }
const vroot = os.dir(vexe) const vroot = os.dir(vexe)
const allowed_formats = ['md', 'markdown', 'json', 'text', 'stdout', 'html', 'htm'] const allowed_formats = ['md', 'markdown', 'json', 'text', 'ansi', 'html', 'htm']
struct Config {
mut:
pub_only bool = true
show_loc bool // for plaintext
is_color bool
is_multi bool
is_vlib bool
is_verbose bool
include_readme bool
include_examples bool = true
include_comments bool // for plaintext
inline_assets bool
theme_dir string = default_theme
no_timestamp bool
output_path string
output_type OutputType = .unset
input_path string
symbol_name string
platform doc.Platform
run_examples bool // `-run-examples` will run all `// Example: assert mod.abc() == y` comments in the processed modules
// The options below are useful for generating a more stable HMTL, that is easier to regression test:
html_only_contents bool // `-html-only-contents` will produce only the content of any given page, without styling tags etc.
html_no_vhash bool // `-html-no-vhash` will remove the version hash from the generated html
html_no_assets bool // `-html-no-assets` will not include CSS and JS asset tags in the generated html
html_no_right bool // `-html-no-right` will not add the doc-toc right panel in the generated html
html_no_toc_urls bool // `-html-no-toc-urls` will not add the toc_links panel in the generated html
html_no_footer bool // `-html-no-footer` will not add the footer panel in the generated html
}
fn main() { fn main() {
if os.args.len < 2 || '-h' in os.args || '-help' in os.args || '--help' in os.args if os.args.len < 2 || '-h' in os.args || '-help' in os.args || '--help' in os.args
@ -27,9 +56,7 @@ fn main() {
// Config is immutable from this point on // Config is immutable from this point on
mut vd := &VDoc{ mut vd := &VDoc{
cfg: cfg cfg: cfg
manifest: vmod.Manifest{ manifest: vmod.Manifest{}
repo_url: ''
}
} }
vd.vprintln('Setting output type to "${cfg.output_type}"') vd.vprintln('Setting output type to "${cfg.output_type}"')
vd.generate_docs_from_file() vd.generate_docs_from_file()
@ -50,6 +77,7 @@ fn main() {
fn parse_arguments(args []string) Config { fn parse_arguments(args []string) Config {
mut cfg := Config{} mut cfg := Config{}
cfg.is_color = term.can_show_color_on_stdout() cfg.is_color = term.can_show_color_on_stdout()
mut is_color_was_set_explicitly := false
for i := 0; i < args.len; i++ { for i := 0; i < args.len; i++ {
arg := args[i] arg := args[i]
current_args := args[i..] current_args := args[i..]
@ -69,9 +97,11 @@ fn parse_arguments(args []string) Config {
} }
'-color' { '-color' {
cfg.is_color = true cfg.is_color = true
is_color_was_set_explicitly = true
} }
'-no-color' { '-no-color' {
cfg.is_color = false cfg.is_color = false
is_color_was_set_explicitly = true
} }
'-inline-assets' { '-inline-assets' {
cfg.inline_assets = true cfg.inline_assets = true
@ -90,7 +120,7 @@ fn parse_arguments(args []string) Config {
} }
'-o' { '-o' {
opath := cmdline.option(current_args, '-o', '') opath := cmdline.option(current_args, '-o', '')
cfg.output_path = if opath == 'stdout' { opath } else { os.real_path(opath) } cfg.output_path = if opath in ['stdout', '-'] { opath } else { os.real_path(opath) }
i++ i++
} }
'-os' { '-os' {
@ -115,6 +145,26 @@ fn parse_arguments(args []string) Config {
'-no-examples' { '-no-examples' {
cfg.include_examples = false cfg.include_examples = false
} }
//
'-html-only-contents' {
cfg.html_only_contents = true
}
'-html-no-vhash' {
cfg.html_no_vhash = true
}
'-html-no-assets' {
cfg.html_no_assets = true
}
'-html-no-right' {
cfg.html_no_right = true
}
'-html-no-toc-urls' {
cfg.html_no_toc_urls = true
}
'-html-no-footer' {
cfg.html_no_footer = true
}
//
'-readme' { '-readme' {
cfg.include_readme = true cfg.include_readme = true
} }
@ -135,28 +185,42 @@ fn parse_arguments(args []string) Config {
} }
} }
} }
// Correct from configuration from user input
if cfg.output_path == 'stdout' && cfg.output_type == .html { if cfg.output_type == .html {
cfg.inline_assets = true // quirks specific to *just* the html output mode:
if cfg.output_path in ['stdout', '-'] {
cfg.inline_assets = true
}
} }
$if windows {
cfg.input_path = cfg.input_path.replace('/', os.path_separator) if !is_color_was_set_explicitly {
} $else { if cfg.output_type == .plaintext {
cfg.input_path = cfg.input_path.replace('\\', os.path_separator) cfg.is_color = false
} else if cfg.output_type == .ansi {
cfg.is_color = true
}
} }
is_path := cfg.input_path.ends_with('.v') || cfg.input_path.split(os.path_separator).len > 1
if cfg.is_color {
os.setenv('VCOLORS', 'always', true)
} else {
os.setenv('VCOLORS', 'never', true)
}
cfg.input_path = cfg.input_path.replace('\\', '/')
is_path := cfg.input_path.ends_with('.v') || cfg.input_path.split('/').len > 1
|| cfg.input_path == '.' || cfg.input_path == '.'
if cfg.input_path.trim_right('/') == 'vlib' { if cfg.input_path.trim_right('/') == 'vlib' {
cfg.is_vlib = true cfg.is_vlib = true
cfg.is_multi = true cfg.is_multi = true
cfg.input_path = os.join_path(vroot, 'vlib') cfg.input_path = os.join_path(vroot, 'vlib')
} else if !is_path { } else if !is_path {
// TODO vd.vprintln('Input "$cfg.input_path" is not a valid path. Looking for modules named "$cfg.input_path"...')
mod_path := doc.lookup_module(cfg.input_path) or { mod_path := doc.lookup_module(cfg.input_path) or {
eprintln('vdoc: ${err}') eprintln('vdoc: ${err}')
exit(1) exit(1)
} }
cfg.input_path = mod_path cfg.input_path = mod_path
} }
cfg.input_path = cfg.input_path.replace('/', os.path_separator)
return cfg return cfg
} }

View File

@ -19,4 +19,4 @@ fn funky()
| foo bar | yes | | foo bar | yes |
|-----------|--------| |-----------|--------|
| working | yup | | working | yup |

View File

@ -1,3 +1,3 @@
module main module main
fn funky() fn funky()

View File

@ -0,0 +1,36 @@
module main
const omega = 3 // should be first
const alpha = 5 // should be in the middle
const beta = 2 // should be at the end
fn abc()
abc - should be last
fn def()
def - should be first
fn xyz()
xyz - should be in the middle
fn MyXMLDocument.abc(text string) ?(string, int)
MyXMLDocument.abc does something too... I just do not know what.
fn MyXMLDocument.from_file(path string) !MyXMLDocument
MyXMLDocument.from_text processes the file path, and returns an error
fn MyXMLDocument.from_text(text string) ?MyXMLDocument
MyXMLDocument.from_text processes text and produces none
struct MyXMLDocument {
path string
}
MyXMLDocument is here just to test the different combinations of methods/output types
fn (x &MyXMLDocument) instance_from_file(path string) !MyXMLDocument
instance_from_file does stuff with path
fn (x &MyXMLDocument) instance_from_text(text string) ?MyXMLDocument
instance_from_text does stuff with text
fn (x &MyXMLDocument) instance_abc(text string) ?(string, int)
instance_abc does stuff too
fn (x &MyXMLDocument) instance_void()
instance_void does stuff too
fn (x &MyXMLDocument) instance_int() int
instance_int does stuff too
fn (x &MyXMLDocument) instance_result() !
instance_error does stuff too
fn (x &MyXMLDocument) instance_option() ?
instance_option does stuff too

View File

@ -0,0 +1,96 @@
<section id="readme_main" class="doc-node">
<div class="title"><h1> main <a href="#readme_main">#</a></h1></div>
</section>
<section id="Constants" class="doc-node const">
<div class="title"><h2>Constants <a href="#Constants">#</a></h2></div>
</section>
<section id="" class="doc-node const">
<pre class="signature"><code><span class="token keyword">const</span> omega <span class="token operator">=</span> <span class="token number">3</span> <span class="token comment">// should be first</span></code></pre>
</section>
<section id="" class="doc-node const">
<pre class="signature"><code><span class="token keyword">const</span> alpha <span class="token operator">=</span> <span class="token number">5</span> <span class="token comment">// should be in the middle</span></code></pre>
</section>
<section id="" class="doc-node const">
<pre class="signature"><code><span class="token keyword">const</span> beta <span class="token operator">=</span> <span class="token number">2</span> <span class="token comment">// should be at the end</span></code></pre>
</section>
<section id="abc" class="doc-node">
<div class="title"><h2>fn abc <a href="#abc">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> <span class="token function">abc</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre>
<p>abc - should be last</p>
</section>
<section id="def" class="doc-node">
<div class="title"><h2>fn def <a href="#def">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> <span class="token function">def</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre>
<p>def - should be first</p>
</section>
<section id="xyz" class="doc-node">
<div class="title"><h2>fn xyz <a href="#xyz">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> <span class="token function">xyz</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre>
<p>xyz - should be in the middle</p>
</section>
<section id="MyXMLDocument.abc" class="doc-node">
<div class="title"><h2>fn MyXMLDocument.abc <a href="#MyXMLDocument.abc">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> MyXMLDocument<span class="token punctuation">.</span><span class="token function">abc</span><span class="token punctuation">(</span>text <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">?</span><span class="token punctuation">(</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">int</span><span class="token punctuation">)</span></code></pre>
<p>MyXMLDocument.abc does something too... I just do not know what.</p>
</section>
<section id="MyXMLDocument.from_file" class="doc-node">
<div class="title"><h2>fn MyXMLDocument.from_file <a href="#MyXMLDocument.from_file">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> MyXMLDocument<span class="token punctuation">.</span><span class="token function">from_file</span><span class="token punctuation">(</span>path <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">!</span>MyXMLDocument</code></pre>
<p>MyXMLDocument.from_text processes the file path, and returns an error</p>
</section>
<section id="MyXMLDocument.from_text" class="doc-node">
<div class="title"><h2>fn MyXMLDocument.from_text <a href="#MyXMLDocument.from_text">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> MyXMLDocument<span class="token punctuation">.</span><span class="token function">from_text</span><span class="token punctuation">(</span>text <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">?</span>MyXMLDocument</code></pre>
<p>MyXMLDocument.from_text processes text and produces none</p>
</section>
<section id="MyXMLDocument" class="doc-node">
<div class="title"><h2>struct MyXMLDocument <a href="#MyXMLDocument">#</a></h2></div><pre class="signature"><code><span class="token keyword">struct</span> <span class="token symbol">MyXMLDocument</span> <span class="token punctuation">{</span>
path <span class="token builtin">string</span>
<span class="token punctuation">}</span></code></pre>
<p>MyXMLDocument is here just to test the different combinations of methods/output types</p>
</section>
<section id="MyXMLDocument.instance_from_file" class="doc-node">
<div class="title"><h2>fn (MyXMLDocument) instance_from_file <a href="#MyXMLDocument.instance_from_file">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> <span class="token punctuation">(</span>x <span class="token operator">&</span>MyXMLDocument<span class="token punctuation">)</span> <span class="token function">instance_from_file</span><span class="token punctuation">(</span>path <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">!</span>MyXMLDocument</code></pre>
<p>instance_from_file does stuff with path</p>
</section>
<section id="MyXMLDocument.instance_from_text" class="doc-node">
<div class="title"><h2>fn (MyXMLDocument) instance_from_text <a href="#MyXMLDocument.instance_from_text">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> <span class="token punctuation">(</span>x <span class="token operator">&</span>MyXMLDocument<span class="token punctuation">)</span> <span class="token function">instance_from_text</span><span class="token punctuation">(</span>text <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">?</span>MyXMLDocument</code></pre>
<p>instance_from_text does stuff with text</p>
</section>
<section id="MyXMLDocument.instance_abc" class="doc-node">
<div class="title"><h2>fn (MyXMLDocument) instance_abc <a href="#MyXMLDocument.instance_abc">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> <span class="token punctuation">(</span>x <span class="token operator">&</span>MyXMLDocument<span class="token punctuation">)</span> <span class="token function">instance_abc</span><span class="token punctuation">(</span>text <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">?</span><span class="token punctuation">(</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">int</span><span class="token punctuation">)</span></code></pre>
<p>instance_abc does stuff too</p>
</section>
<section id="MyXMLDocument.instance_void" class="doc-node">
<div class="title"><h2>fn (MyXMLDocument) instance_void <a href="#MyXMLDocument.instance_void">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> <span class="token punctuation">(</span>x <span class="token operator">&</span>MyXMLDocument<span class="token punctuation">)</span> <span class="token function">instance_void</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre>
<p>instance_void does stuff too</p>
</section>
<section id="MyXMLDocument.instance_int" class="doc-node">
<div class="title"><h2>fn (MyXMLDocument) instance_int <a href="#MyXMLDocument.instance_int">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> <span class="token punctuation">(</span>x <span class="token operator">&</span>MyXMLDocument<span class="token punctuation">)</span> <span class="token function">instance_int</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">int</span></code></pre>
<p>instance_int does stuff too</p>
</section>
<section id="MyXMLDocument.instance_result" class="doc-node">
<div class="title"><h2>fn (MyXMLDocument) instance_result <a href="#MyXMLDocument.instance_result">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> <span class="token punctuation">(</span>x <span class="token operator">&</span>MyXMLDocument<span class="token punctuation">)</span> <span class="token function">instance_result</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!</span></code></pre>
<p>instance_error does stuff too</p>
</section>
<section id="MyXMLDocument.instance_option" class="doc-node">
<div class="title"><h2>fn (MyXMLDocument) instance_option <a href="#MyXMLDocument.instance_option">#</a></h2></div><pre class="signature"><code><span class="token keyword">fn</span> <span class="token punctuation">(</span>x <span class="token operator">&</span>MyXMLDocument<span class="token punctuation">)</span> <span class="token function">instance_option</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">?</span></code></pre>
<p>instance_option does stuff too</p>
</section>

View File

@ -0,0 +1,35 @@
module main
const omega = 3 // should be first
const alpha = 5 // should be in the middle
const beta = 2 // should be at the end
fn abc()
abc - should be last
fn def()
def - should be first
fn xyz()
xyz - should be in the middle
fn MyXMLDocument.abc(text string) ?(string, int)
MyXMLDocument.abc does something too... I just do not know what.
fn MyXMLDocument.from_file(path string) !MyXMLDocument
MyXMLDocument.from_text processes the file path, and returns an error
fn MyXMLDocument.from_text(text string) ?MyXMLDocument
MyXMLDocument.from_text processes text and produces none
struct MyXMLDocument {
path string
}
MyXMLDocument is here just to test the different combinations of methods/output types
fn (x &MyXMLDocument) instance_from_file(path string) !MyXMLDocument
instance_from_file does stuff with path
fn (x &MyXMLDocument) instance_from_text(text string) ?MyXMLDocument
instance_from_text does stuff with text
fn (x &MyXMLDocument) instance_abc(text string) ?(string, int)
instance_abc does stuff too
fn (x &MyXMLDocument) instance_void()
instance_void does stuff too
fn (x &MyXMLDocument) instance_int() int
instance_int does stuff too
fn (x &MyXMLDocument) instance_result() !
instance_error does stuff too
fn (x &MyXMLDocument) instance_option() ?
instance_option does stuff too

View File

@ -0,0 +1,75 @@
pub const omega = 3 // should be first
pub const alpha = 5 // should be in the middle
pub const beta = 2 // should be at the end
// def - should be first
pub fn def() {
println(1)
}
// xyz - should be in the middle
pub fn xyz() {
println(2)
}
// abc - should be last
pub fn abc() {
println(3)
}
// MyXMLDocument is here just to test the different combinations of methods/output types
pub struct MyXMLDocument {
path string
}
// MyXMLDocument.from_text processes the file path, and returns an error
pub fn MyXMLDocument.from_file(path string) !MyXMLDocument {
return error('TODO')
}
// MyXMLDocument.from_text processes text and produces none
pub fn MyXMLDocument.from_text(text string) ?MyXMLDocument {
return none
}
// MyXMLDocument.abc does something too... I just do not know what.
pub fn MyXMLDocument.abc(text string) ?(string, int) {
return 'xyz', 123
}
// instance_from_file does stuff with path
pub fn (x &MyXMLDocument) instance_from_file(path string) !MyXMLDocument {
return error('TODO')
}
// instance_from_text does stuff with text
pub fn (x &MyXMLDocument) instance_from_text(text string) ?MyXMLDocument {
return none
}
// instance_abc does stuff too
pub fn (x &MyXMLDocument) instance_abc(text string) ?(string, int) {
return 'xyz', 123
}
// instance_void does stuff too
pub fn (x &MyXMLDocument) instance_void() {
return 123
}
// instance_int does stuff too
pub fn (x &MyXMLDocument) instance_int() int {
return 123
}
// instance_error does stuff too
pub fn (x &MyXMLDocument) instance_result() ! {
return 123
}
// instance_option does stuff too
pub fn (x &MyXMLDocument) instance_option() ? {
return 123
}

View File

@ -1,3 +1,3 @@
module foo module foo
fn bar() fn bar()

View File

@ -1,4 +1,4 @@
hello from readme hello from readme
module foo module foo
fn bar() fn bar()

View File

@ -10,6 +10,8 @@ const vroot = @VMODROOT
const diff_cmd = find_diff_cmd() const diff_cmd = find_diff_cmd()
const should_autofix = os.getenv('VAUTOFIX') != ''
fn find_diff_cmd() string { fn find_diff_cmd() string {
return diff.find_working_diff_command() or { '' } return diff.find_working_diff_command() or { '' }
} }
@ -82,6 +84,23 @@ fn check_path(vexe string, dir string, tests []string) int {
cmd: '${os.quoted_path(vexe)} doc -readme -comments ${os.quoted_path(program)}' cmd: '${os.quoted_path(vexe)} doc -readme -comments ${os.quoted_path(program)}'
out_filename: 'main.readme.comments.out' out_filename: 'main.readme.comments.out'
) )
// test the main 3 different formats:
fails += check_output(
program: program
cmd: '${os.quoted_path(vexe)} doc -f html -o - -html-only-contents -readme -comments ${os.quoted_path(program)}'
out_filename: 'main.html'
)
fails += check_output(
program: program
cmd: '${os.quoted_path(vexe)} doc -f ansi -o - -html-only-contents -readme -comments ${os.quoted_path(program)}'
out_filename: 'main.ansi'
)
fails += check_output(
program: program
cmd: '${os.quoted_path(vexe)} doc -f text -o - -html-only-contents -readme -comments ${os.quoted_path(program)}'
out_filename: 'main.text'
)
//
total_fails += fails total_fails += fails
if fails == 0 { if fails == 0 {
println(term.green('OK')) println(term.green('OK'))
@ -141,9 +160,13 @@ fn check_output(params CheckOutputParams) int {
found := clean_line_endings(res.output) found := clean_line_endings(res.output)
if expected != found { if expected != found {
print_compare(expected, found) print_compare(expected, found)
eprintln('>>> out_file_path: ${out_file_path}')
eprintln('>>> cmd: VDOC_SORT=${params.should_sort} ${params.cmd}') eprintln('>>> cmd: VDOC_SORT=${params.should_sort} ${params.cmd}')
eprintln('>>> out_file_path: `${out_file_path}`')
eprintln('>>> fix: VDOC_SORT=${params.should_sort} ${params.cmd} > ${out_file_path}')
fails++ fails++
} }
if should_autofix {
os.write_file(out_file_path, res.output) or {}
}
return fails return fails
} }

View File

@ -60,14 +60,14 @@ fn trim_doc_node_description(description string) string {
} }
fn set_output_type_from_str(format string) OutputType { fn set_output_type_from_str(format string) OutputType {
output_type := match format { return match format {
'htm', 'html' { OutputType.html } 'htm', 'html' { OutputType.html }
'md', 'markdown' { OutputType.markdown } 'md', 'markdown' { .markdown }
'json' { OutputType.json } 'json' { .json }
'stdout' { OutputType.stdout } 'text' { .plaintext }
else { OutputType.plaintext } 'ansi' { .ansi }
else { .ansi }
} }
return output_type
} }
fn get_ignore_paths(path string) ![]string { fn get_ignore_paths(path string) ![]string {
@ -213,10 +213,13 @@ fn color_highlight(code string, tb &ast.Table) string {
} else if } else if
(next_tok.kind in [.lcbr, .rpar, .eof, .comma, .pipe, .name, .rcbr, .assign, .key_pub, .key_mut, .pipe, .comma, .comment, .lt, .lsbr] (next_tok.kind in [.lcbr, .rpar, .eof, .comma, .pipe, .name, .rcbr, .assign, .key_pub, .key_mut, .pipe, .comma, .comment, .lt, .lsbr]
&& next_tok.lit !in builtin) && next_tok.lit !in builtin)
&& (prev.kind in [.name, .amp, .lcbr, .rsbr, .key_type, .assign, .dot, .question, .rpar, .key_struct, .key_enum, .pipe, .key_interface, .comment, .ellipsis] && (prev.kind in [.name, .amp, .lcbr, .rsbr, .key_type, .assign, .dot, .not, .question, .rpar, .key_struct, .key_enum, .pipe, .key_interface, .comment, .ellipsis, .comma]
&& prev.lit !in builtin) && ((tok.lit != '' && tok.lit[0].is_capital()) && prev.lit !in builtin) && ((tok.lit != '' && tok.lit[0].is_capital())
|| prev_prev.lit in ['C', 'JS']) { || prev_prev.lit in ['C', 'JS']) {
tok_typ = .symbol tok_typ = .symbol
} else if tok.lit[0].is_capital() && prev.kind == .lpar
&& next_tok.kind == .comma {
tok_typ = .symbol
} else if next_tok.kind == .lpar } else if next_tok.kind == .lpar
|| (!(tok.lit != '' && tok.lit[0].is_capital()) || (!(tok.lit != '' && tok.lit[0].is_capital())
&& next_tok.kind in [.lt, .lsbr] && next_tok.pos == tok.pos + tok.lit.len) { && next_tok.kind in [.lt, .lsbr] && next_tok.pos == tok.pos + tok.lit.len) {

View File

@ -17,8 +17,8 @@ enum OutputType {
html html
markdown markdown
json json
ansi // text with ANSI color escapes
plaintext plaintext
stdout
} }
@[heap] @[heap]
@ -36,28 +36,6 @@ mut:
example_oks int // how many ok examples were found when `-run-examples` was passed, that compiled and finished with 0 exit code. example_oks int // how many ok examples were found when `-run-examples` was passed, that compiled and finished with 0 exit code.
} }
struct Config {
mut:
pub_only bool = true
show_loc bool // for plaintext
is_color bool
is_multi bool
is_vlib bool
is_verbose bool
include_readme bool
include_examples bool = true
include_comments bool // for plaintext
inline_assets bool
theme_dir string = default_theme
no_timestamp bool
output_path string
output_type OutputType = .unset
input_path string
symbol_name string
platform doc.Platform
run_examples bool // `-run-examples` will run all `// Example: assert mod.abc() == y` comments in the processed modules
}
// //
struct Output { struct Output {
mut: mut:
@ -279,7 +257,7 @@ fn (mut vd VDoc) generate_docs_from_file() {
} }
if out.path.len == 0 { if out.path.len == 0 {
if cfg.output_type == .unset { if cfg.output_type == .unset {
out.typ = .stdout out.typ = .ansi
} else { } else {
vd.vprintln('No output path has detected. Using input path instead.') vd.vprintln('No output path has detected. Using input path instead.')
out.path = cfg.input_path out.path = cfg.input_path
@ -289,7 +267,7 @@ fn (mut vd VDoc) generate_docs_from_file() {
ext := os.file_ext(out.path) ext := os.file_ext(out.path)
out.typ = set_output_type_from_str(ext.all_after('.')) out.typ = set_output_type_from_str(ext.all_after('.'))
} }
if cfg.include_readme && out.typ !in [.html, .stdout] { if cfg.include_readme && out.typ !in [.html, .ansi, .plaintext] {
eprintln('vdoc: Including README.md for doc generation is supported on HTML output, or when running directly in the terminal.') eprintln('vdoc: Including README.md for doc generation is supported on HTML output, or when running directly in the terminal.')
exit(1) exit(1)
} }
@ -312,7 +290,7 @@ fn (mut vd VDoc) generate_docs_from_file() {
comment := doc.DocComment{ comment := doc.DocComment{
text: readme_contents text: readme_contents
} }
if out.typ == .stdout { if out.typ == .ansi {
println(markdown.to_plain(readme_contents)) println(markdown.to_plain(readme_contents))
} else if out.typ == .html && cfg.is_multi { } else if out.typ == .html && cfg.is_multi {
vd.docs << doc.Doc{ vd.docs << doc.Doc{
@ -365,7 +343,7 @@ fn (mut vd VDoc) generate_docs_from_file() {
exit(1) exit(1)
} }
vd.vprintln('Rendering docs...') vd.vprintln('Rendering docs...')
if out.path.len == 0 || out.path == 'stdout' { if out.path.len == 0 || out.path == 'stdout' || out.path == '-' {
if out.typ == .html { if out.typ == .html {
vd.render_static_html(out) vd.render_static_html(out)
} }

View File

@ -11,22 +11,20 @@ Examples:
v doc -m -f html vlib/ v doc -m -f html vlib/
Generates the documentation of a given MODULE, DIRECTORY, or FILE Generates the documentation of a given MODULE, DIRECTORY, or FILE
and prints or saves them to its desired format. It can generate HTML, JSON, and prints or saves them to its desired format: HTML, JSON,
or Markdown format. TEXT, ANSI or Markdown.
Options: Options:
-all Includes private and public functions/methods/structs/consts/enums. -all Includes private and public functions/methods/structs/consts/enums.
-color Forces the use of ANSI escape sequences to make the output colorful.
-no-color Forces plain text output, without ANSI colors.
Note: -color is on for -f ansi .
-f Specifies the output format to be used. -f Specifies the output format to be used.
Available formats are: Available formats are: md/markdown, json, text, ansi and html/htm.
md/markdown, json, text, stdout and html/htm
-h, -help Prints this help text. -h, -help Prints this help text.
-m Generate docs for modules listed in that folder. -m Generate docs for modules listed in that folder.
-o Specifies the output file/folder path where to store the -o The output file/folder path where to store the docs. Use `-o stdout`
generated docs. or `-o -', to print the output instead of saving it to a file.
Set it to "stdout" to print the output instead of saving
the contents to a file.
-color Forces stdout colorize output.
-no-color Forces plain text output, without ANSI colors.
-readme 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.
-no-timestamp Omits the timestamp in the output file. -no-timestamp Omits the timestamp in the output file.
@ -36,7 +34,18 @@ For HTML mode:
webpage directly. webpage directly.
-theme-dir The directory for doc theme template -theme-dir The directory for doc theme template
For plain text mode: The following options are useful for tests, that need stable output.
They will omit generating text that is prone to changes, due to styling,
but that otherwise do not affect the content.
-html-only-contents Produce just the main content of the page,
without theming, styling, CSS and JS tags etc.
-html-no-vhash Omits the version hash.
-html-no-assets Omits the CSS and JS asset tags.
-html-no-right Omits the doc-toc right panel.
-html-no-toc-urls Omits the toc_links panel
-html-no-footer Omits the footer panel.
For the text and ansi modes:
-l Shows the locations of the generated signatures. -l Shows the locations of the generated signatures.
-comments Includes comments in the output. -comments Includes comments in the output.