aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/old/cmd/tools/vdoc
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/old/cmd/tools/vdoc')
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/html.v553
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/html_tag_escape_test.v6
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/markdown.v55
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/arrow.svg1
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/dark-mode.js6
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/dark.svg1
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/doc.css725
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/doc.js235
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-192x192.pngbin0 -> 6083 bytes
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-512x512.pngbin0 -> 18209 bytes
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/apple-touch-icon.pngbin0 -> 5707 bytes
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/browserconfig.xml9
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-16x16.pngbin0 -> 853 bytes
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-32x32.pngbin0 -> 1305 bytes
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon.icobin0 -> 15086 bytes
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-144x144.pngbin0 -> 4512 bytes
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-150x150.pngbin0 -> 4360 bytes
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x150.pngbin0 -> 4927 bytes
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x310.pngbin0 -> 10195 bytes
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-70x70.pngbin0 -> 3093 bytes
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/safari-pinned-tab.svg39
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/favicons/site.webmanifest19
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/light.svg1
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/link.svg1
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/menu.svg1
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/resources/normalize.css171
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.out1
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.v8
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/tests/vdoc_file_test.v72
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/utils.v275
-rw-r--r--v_windows/v/old/cmd/tools/vdoc/vdoc.v511
31 files changed, 2690 insertions, 0 deletions
diff --git a/v_windows/v/old/cmd/tools/vdoc/html.v b/v_windows/v/old/cmd/tools/vdoc/html.v
new file mode 100644
index 0000000..e253898
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/html.v
@@ -0,0 +1,553 @@
+module main
+
+import os
+import net.urllib
+import strings
+import markdown
+import regex
+import v.scanner
+import v.ast
+import v.token
+import v.doc
+import v.pref
+
+const (
+ css_js_assets = ['doc.css', 'normalize.css', 'doc.js', 'dark-mode.js']
+ res_path = os.resource_abs_path('resources')
+ favicons_path = os.join_path(res_path, 'favicons')
+ link_svg = '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></svg>'
+ 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 rel="preconnect" href="https://fonts.gstatic.com">
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
+ <link href="https://fonts.googleapis.com/css2?family=Work+Sans:wght@400;500;600&display=swap" rel="stylesheet">
+ <link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
+ <link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
+ <link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
+ <link rel="manifest" href="site.webmanifest">
+ <link rel="mask-icon" href="safari-pinned-tab.svg" color="#5bbad5">
+ <meta name="msapplication-TileColor" content="#da532c">
+ <meta name="theme-color" content="#ffffff">
+ {{ head_assets }}
+ </head>
+ <body>
+ <div id="page">
+ <header class="doc-nav hidden">
+ <div class="heading-container">
+ <div class="heading">
+ <div class="info">
+ <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>
+ <input type="text" id="search" placeholder="Search... (beta)" autocomplete="off">
+ </div>
+ </div>
+ <nav class="search hidden"></nav>
+ <nav class="content hidden">
+ <ul>
+ {{ toc_links }}
+ </ul>
+ </nav>
+ </header>
+ <div class="doc-scrollview">
+ <div class="doc-container">
+ <div class="doc-content">
+{{ contents }}
+ <div class="footer">
+ {{ footer_content }}
+ </div>
+ </div>
+ {{ right_content }}
+ </div>
+ </div>
+ </div>
+ {{ footer_assets }}
+ <script async src="search_index.js" type="text/javascript"></script>
+ </body>
+</html>'
+)
+
+enum HighlightTokenTyp {
+ unone
+ boolean
+ builtin
+ char
+ comment
+ function
+ keyword
+ name
+ number
+ operator
+ punctuation
+ string
+ symbol
+ none_
+ module_
+ prefix
+}
+
+struct SearchModuleResult {
+ description string
+ link string
+}
+
+struct SearchResult {
+ prefix string
+ badge string
+ description string
+ link string
+}
+
+fn (vd VDoc) render_search_index(out Output) {
+ mut js_search_index := strings.new_builder(200)
+ mut js_search_data := strings.new_builder(200)
+ js_search_index.write_string('var searchModuleIndex = [')
+ js_search_data.write_string('var searchModuleData = [')
+ for i, title in vd.search_module_index {
+ data := vd.search_module_data[i]
+ js_search_index.write_string('"$title",')
+ js_search_data.write_string('["$data.description","$data.link"],')
+ }
+ js_search_index.writeln('];')
+ js_search_index.write_string('var searchIndex = [')
+ js_search_data.writeln('];')
+ js_search_data.write_string('var searchData = [')
+ for i, title in vd.search_index {
+ data := vd.search_data[i]
+ js_search_index.write_string('"$title",')
+ // array instead of object to reduce file size
+ js_search_data.write_string('["$data.badge","$data.description","$data.link","$data.prefix"],')
+ }
+ js_search_index.writeln('];')
+ js_search_data.writeln('];')
+ out_file_path := os.join_path(out.path, 'search_index.js')
+ os.write_file(out_file_path, js_search_index.str() + js_search_data.str()) or { panic(err) }
+}
+
+fn (mut vd VDoc) render_static_html(out Output) {
+ vd.assets = map{
+ 'doc_css': vd.get_resource(css_js_assets[0], out)
+ 'normalize_css': vd.get_resource(css_js_assets[1], out)
+ 'doc_js': vd.get_resource(css_js_assets[2], out)
+ 'dark_mode_js': vd.get_resource(css_js_assets[3], out)
+ 'light_icon': vd.get_resource('light.svg', out)
+ 'dark_icon': vd.get_resource('dark.svg', out)
+ 'menu_icon': vd.get_resource('menu.svg', out)
+ 'arrow_icon': vd.get_resource('arrow.svg', out)
+ }
+}
+
+fn (vd VDoc) get_resource(name string, out Output) string {
+ cfg := vd.cfg
+ path := os.join_path(res_path, name)
+ mut res := os.read_file(path) or { panic('vdoc: could not read $path') }
+ /*
+ if minify {
+ if name.ends_with('.js') {
+ res = js_compress(res)
+ } else {
+ res = res.split_into_lines().map(it.trim_space()).join('')
+ }
+ }
+ */
+ // TODO: Make SVG inline for now
+ if cfg.inline_assets || path.ends_with('.svg') {
+ return res
+ } else {
+ output_path := os.join_path(out.path, name)
+ if !os.exists(output_path) {
+ println('Generating $out.typ in "$output_path"')
+ os.write_file(output_path, res) or { panic(err) }
+ }
+ return name
+ }
+}
+
+fn (mut vd VDoc) collect_search_index(out Output) {
+ cfg := vd.cfg
+ for doc in vd.docs {
+ mod := doc.head.name
+ vd.search_module_index << mod
+ comments := if cfg.include_examples {
+ doc.head.merge_comments()
+ } else {
+ doc.head.merge_comments_without_examples()
+ }
+ vd.search_module_data << SearchModuleResult{
+ description: trim_doc_node_description(comments)
+ link: vd.get_file_name(mod, out)
+ }
+ for _, dn in doc.contents {
+ vd.create_search_results(mod, dn, out)
+ }
+ }
+}
+
+fn (mut vd VDoc) create_search_results(mod string, dn doc.DocNode, out Output) {
+ cfg := vd.cfg
+ if dn.kind == .const_group {
+ return
+ }
+ comments := if cfg.include_examples {
+ dn.merge_comments()
+ } else {
+ dn.merge_comments_without_examples()
+ }
+ dn_description := trim_doc_node_description(comments)
+ vd.search_index << dn.name
+ vd.search_data << SearchResult{
+ prefix: if dn.parent_name != '' { '$dn.kind ($dn.parent_name)' } else { '$dn.kind ' }
+ description: dn_description
+ badge: mod
+ link: vd.get_file_name(mod, out) + '#' + get_node_id(dn)
+ }
+ for child in dn.children {
+ vd.create_search_results(mod, child, out)
+ }
+}
+
+fn (vd VDoc) write_content(cn &doc.DocNode, d &doc.Doc, mut hw strings.Builder) {
+ cfg := vd.cfg
+ base_dir := os.dir(os.real_path(cfg.input_path))
+ 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(vd.manifest.repo_url, file_path_name, cn.pos.line_nr + 1)
+ if cn.content.len != 0 || (cn.name == 'Constants') {
+ hw.write_string(doc_node_html(cn, src_link, false, cfg.include_examples, d.table))
+ }
+ for child in cn.children {
+ child_file_path_name := child.file_path.replace('$base_dir/', '')
+ child_src_link := get_src_link(vd.manifest.repo_url, child_file_path_name,
+ child.pos.line_nr + 1)
+ hw.write_string(doc_node_html(child, child_src_link, false, cfg.include_examples,
+ d.table))
+ }
+}
+
+fn (vd VDoc) gen_html(d doc.Doc) string {
+ cfg := vd.cfg
+ mut symbols_toc := strings.new_builder(200)
+ mut modules_toc := strings.new_builder(200)
+ mut contents := strings.new_builder(200)
+ dcs_contents := d.contents.arr()
+ // generate toc first
+ contents.writeln(doc_node_html(d.head, '', true, cfg.include_examples, d.table))
+ if is_module_readme(d.head) {
+ write_toc(d.head, mut symbols_toc)
+ }
+ for cn in dcs_contents {
+ vd.write_content(&cn, &d, mut contents)
+ write_toc(cn, mut symbols_toc)
+ } // write head
+ // write css
+ version := if vd.manifest.version.len != 0 { vd.manifest.version } else { '' }
+ header_name := if cfg.is_multi && vd.docs.len > 1 {
+ os.file_name(os.real_path(cfg.input_path))
+ } else {
+ d.head.name
+ }
+ // write nav1
+ if cfg.is_multi || vd.docs.len > 1 {
+ mut submod_prefix := ''
+ for i, dc in vd.docs {
+ if i - 1 >= 0 && dc.head.name.starts_with(submod_prefix + '.') {
+ continue
+ }
+ names := dc.head.name.split('.')
+ submod_prefix = if names.len > 1 { names[0] } else { dc.head.name }
+ mut href_name := './${dc.head.name}.html'
+ if (cfg.is_vlib && dc.head.name == 'builtin' && !cfg.include_readme)
+ || dc.head.name == 'README' {
+ href_name = './index.html'
+ } else if submod_prefix !in vd.docs.map(it.head.name) {
+ href_name = '#'
+ }
+ submodules := vd.docs.filter(it.head.name.starts_with(submod_prefix + '.'))
+ dropdown := if submodules.len > 0 { vd.assets['arrow_icon'] } else { '' }
+ active_class := if dc.head.name == d.head.name { ' active' } else { '' }
+ modules_toc.write_string('<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 {
+ modules_toc.write_string('<ul>')
+ }
+ submod_name := cdoc.head.name.all_after(submod_prefix + '.')
+ sub_selected_classes := if cdoc.head.name == d.head.name {
+ ' class="active"'
+ } else {
+ ''
+ }
+ modules_toc.write_string('<li$sub_selected_classes><a href="./${cdoc.head.name}.html">$submod_name</a></li>')
+ if j == submodules.len - 1 {
+ modules_toc.write_string('</ul>')
+ }
+ }
+ modules_toc.write_string('</li>')
+ }
+ }
+ modules_toc_str := modules_toc.str()
+ symbols_toc_str := symbols_toc.str()
+ result := html_content.replace('{{ title }}', d.head.name).replace('{{ head_name }}',
+ header_name).replace('{{ version }}', version).replace('{{ light_icon }}', vd.assets['light_icon']).replace('{{ dark_icon }}',
+ vd.assets['dark_icon']).replace('{{ menu_icon }}', vd.assets['menu_icon']).replace('{{ head_assets }}',
+ if cfg.inline_assets {
+ '\n${tabs[0]}<style>' + vd.assets['doc_css'] + '</style>\n${tabs[0]}<style>' +
+ vd.assets['normalize_css'] + '</style>\n${tabs[0]}<script>' +
+ vd.assets['dark_mode_js'] + '</script>'
+ } else {
+ '\n${tabs[0]}<link rel="stylesheet" href="' + vd.assets['doc_css'] +
+ '" />\n${tabs[0]}<link rel="stylesheet" href="' + vd.assets['normalize_css'] +
+ '" />\n${tabs[0]}<script src="' + vd.assets['dark_mode_js'] + '"></script>'
+ }).replace('{{ toc_links }}', if cfg.is_multi || vd.docs.len > 1 {
+ modules_toc_str
+ } else {
+ symbols_toc_str
+ }).replace('{{ contents }}', contents.str()).replace('{{ right_content }}', if cfg.is_multi
+ && vd.docs.len > 1 && d.head.name != 'README' {
+ '<div class="doc-toc"><ul>' + symbols_toc_str + '</ul></div>'
+ } else {
+ ''
+ }).replace('{{ footer_content }}', gen_footer_text(d, !cfg.no_timestamp)).replace('{{ footer_assets }}',
+ if cfg.inline_assets {
+ '<script>' + vd.assets['doc_js'] + '</script>'
+ } else {
+ '<script src="' + vd.assets['doc_js'] + '"></script>'
+ })
+ return result
+}
+
+fn get_src_link(repo_url string, file_name string, line_nr int) string {
+ mut url := urllib.parse(repo_url) or { return '' }
+ if url.path.len <= 1 || file_name.len == 0 {
+ return ''
+ }
+ url.path = url.path.trim_right('/') + match url.host {
+ 'github.com' { '/blob/master/$file_name' }
+ 'gitlab.com' { '/-/blob/master/$file_name' }
+ 'git.sir.ht' { '/tree/master/$file_name' }
+ else { '' }
+ }
+ if url.path == '/' {
+ return ''
+ }
+ url.fragment = 'L$line_nr'
+ return url.str()
+}
+
+fn html_highlight(code string, tb &ast.Table) string {
+ builtin := ['bool', 'string', 'i8', 'i16', 'int', 'i64', 'i128', 'byte', 'u16', 'u32', 'u64',
+ 'u128', 'rune', 'f32', 'f64', 'int_literal', 'float_literal', 'byteptr', 'voidptr', 'any']
+ highlight_code := fn (tok token.Token, typ HighlightTokenTyp) string {
+ lit := if typ in [.unone, .operator, .punctuation] {
+ tok.kind.str()
+ } else if typ == .string {
+ "'$tok.lit'"
+ } else if typ == .char {
+ '`$tok.lit`'
+ } else {
+ tok.lit
+ }
+ if typ in [.unone, .name] {
+ return lit
+ }
+ return '<span class="token $typ">$lit</span>'
+ }
+ mut s := scanner.new_scanner(code, .parse_comments, &pref.Preferences{})
+ mut tok := s.scan()
+ mut next_tok := s.scan()
+ mut buf := strings.new_builder(200)
+ mut i := 0
+ for i < code.len {
+ if i == tok.pos {
+ mut tok_typ := HighlightTokenTyp.unone
+ match tok.kind {
+ .name {
+ if tok.lit in builtin || tb.known_type(tok.lit) {
+ tok_typ = .builtin
+ } else if next_tok.kind == .lcbr {
+ tok_typ = .symbol
+ } else if next_tok.kind == .lpar {
+ tok_typ = .function
+ } else {
+ tok_typ = .name
+ }
+ }
+ .comment {
+ tok_typ = .comment
+ }
+ .chartoken {
+ tok_typ = .char
+ }
+ .string {
+ tok_typ = .string
+ }
+ .number {
+ tok_typ = .number
+ }
+ .key_true, .key_false {
+ tok_typ = .boolean
+ }
+ .lpar, .lcbr, .rpar, .rcbr, .lsbr, .rsbr, .semicolon, .colon, .comma, .dot {
+ tok_typ = .punctuation
+ }
+ else {
+ if token.is_key(tok.lit) || token.is_decl(tok.kind) {
+ tok_typ = .keyword
+ } else if tok.kind == .decl_assign || tok.kind.is_assign() || tok.is_unary()
+ || tok.kind.is_relational() || tok.kind.is_infix() {
+ tok_typ = .operator
+ }
+ }
+ }
+ buf.write_string(highlight_code(tok, tok_typ))
+ if next_tok.kind != .eof {
+ i = tok.pos + tok.len
+ tok = next_tok
+ next_tok = s.scan()
+ } else {
+ break
+ }
+ } else {
+ buf.write_b(code[i])
+ i++
+ }
+ }
+ return buf.str()
+}
+
+fn doc_node_html(dn doc.DocNode, link string, head bool, include_examples bool, tb &ast.Table) string {
+ mut dnw := strings.new_builder(200)
+ head_tag := if head { 'h1' } else { 'h2' }
+ comments := dn.merge_comments_without_examples()
+ // Allow README.md to go through unescaped except for script tags
+ escaped_html := if head && is_module_readme(dn) {
+ // Strip markdown [TOC] directives, since we generate our own.
+ stripped := comments.replace('[TOC]', '')
+ markdown_escape_script_tags(stripped)
+ } else {
+ html_tag_escape(comments)
+ }
+ md_content := markdown.to_html(escaped_html)
+ highlighted_code := html_highlight(dn.content, tb)
+ node_class := if dn.kind == .const_group { ' const' } else { '' }
+ sym_name := get_sym_name(dn)
+ has_deprecated := 'deprecated' in dn.tags
+ mut tags := dn.tags.filter(it != 'deprecated')
+ tags.sort()
+ mut node_id := get_node_id(dn)
+ mut hash_link := if !head { ' <a href="#$node_id">#</a>' } else { '' }
+ if head && is_module_readme(dn) {
+ node_id = 'readme_$node_id'
+ hash_link = ' <a href="#$node_id">#</a>'
+ }
+ dnw.writeln('${tabs[1]}<section id="$node_id" class="doc-node$node_class">')
+ if dn.name.len > 0 {
+ if dn.kind == .const_group {
+ dnw.write_string('${tabs[2]}<div class="title"><$head_tag>$sym_name$hash_link</$head_tag>')
+ } else {
+ dnw.write_string('${tabs[2]}<div class="title"><$head_tag>$dn.kind $sym_name$hash_link</$head_tag>')
+ }
+ if link.len != 0 {
+ dnw.write_string('<a class="link" rel="noreferrer" target="_blank" href="$link">$link_svg</a>')
+ }
+ dnw.write_string('</div>')
+ }
+ if tags.len > 0 || has_deprecated {
+ mut attributes := if has_deprecated {
+ '<div class="attribute attribute-deprecated">deprecated</div>'
+ } else {
+ ''
+ }
+ attributes += tags.map('<div class="attribute">$it</div>').join('')
+ dnw.writeln('<div class="attributes">$attributes</div>')
+ }
+ if !head && dn.content.len > 0 {
+ dnw.writeln('<pre class="signature"><code>$highlighted_code</code></pre>')
+ }
+ // do not mess with md_content further, its formatting is important, just output it 1:1 !
+ dnw.writeln('$md_content\n')
+ // Write examples if any found
+ examples := dn.examples()
+ if include_examples && examples.len > 0 {
+ example_title := if examples.len > 1 { 'Examples' } else { 'Example' }
+ dnw.writeln('<section class="doc-node examples"><h4>$example_title</h4>')
+ for example in examples {
+ // hl_example := html_highlight(example, tb)
+ dnw.writeln('<pre><code class="language-v">$example</code></pre>')
+ }
+ dnw.writeln('</section>')
+ }
+ dnw.writeln('</section>')
+ dnw_str := dnw.str()
+ return dnw_str
+}
+
+fn html_tag_escape(str string) string {
+ excaped_string := str.replace_each(['<', '&lt;', '>', '&gt;'])
+ mut re := regex.regex_opt(r'`.+[(&lt;)(&gt;)].+`') or { regex.RE{} }
+ if re.find_all_str(excaped_string).len > 0 {
+ return str
+ }
+ return excaped_string
+}
+
+/*
+fn js_compress(str string) string {
+ mut js := strings.new_builder(200)
+ lines := str.split_into_lines()
+ 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 0 .. rules.len - 1 {
+ trimmed = trimmed.replace(rules[i], clean[i])
+ }
+ js.write_string(trimmed)
+ }
+ js_str := js.str()
+ return js_str
+}
+*/
+fn write_toc(dn doc.DocNode, mut toc strings.Builder) {
+ mut toc_slug := if dn.name.len == 0 || dn.content.len == 0 { '' } else { slug(dn.name) }
+ if toc_slug == '' && dn.children.len > 0 {
+ if dn.children[0].name == '' {
+ toc_slug = slug(dn.name)
+ } else {
+ toc_slug = slug(dn.name + '.' + dn.children[0].name)
+ }
+ }
+ if is_module_readme(dn) {
+ if dn.comments.len == 0 || (dn.comments.len > 0 && dn.comments[0].text.len == 0) {
+ return
+ }
+ toc.write_string('<li class="open"><a href="#readme_$toc_slug">README</a>')
+ } else if dn.name != 'Constants' {
+ toc.write_string('<li class="open"><a href="#$toc_slug">$dn.kind $dn.name</a>')
+ toc.writeln(' <ul>')
+ for child in dn.children {
+ cname := dn.name + '.' + child.name
+ toc.writeln('<li><a href="#${slug(cname)}">$child.kind $child.name</a></li>')
+ }
+ toc.writeln('</ul>')
+ } else {
+ toc.write_string('<li class="open"><a href="#$toc_slug">$dn.name</a>')
+ }
+ toc.writeln('</li>')
+}
diff --git a/v_windows/v/old/cmd/tools/vdoc/html_tag_escape_test.v b/v_windows/v/old/cmd/tools/vdoc/html_tag_escape_test.v
new file mode 100644
index 0000000..64a7d1c
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/html_tag_escape_test.v
@@ -0,0 +1,6 @@
+module main
+
+fn test_html_tag_escape() {
+ assert html_tag_escape('<abc>') == '&lt;abc&gt;'
+ assert html_tag_escape('`<abc>`') == '`<abc>`'
+}
diff --git a/v_windows/v/old/cmd/tools/vdoc/markdown.v b/v_windows/v/old/cmd/tools/vdoc/markdown.v
new file mode 100644
index 0000000..cc24ad2
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/markdown.v
@@ -0,0 +1,55 @@
+module main
+
+import strings
+import v.doc
+
+fn markdown_escape_script_tags(str string) string {
+ return str.replace_each(['<script>', '`', '</script>', '`'])
+}
+
+fn (vd VDoc) gen_markdown(d doc.Doc, with_toc bool) string {
+ mut hw := strings.new_builder(200)
+ mut cw := strings.new_builder(200)
+ hw.writeln('# $d.head.content\n')
+ if d.head.comments.len > 0 {
+ comments := if vd.cfg.include_examples {
+ d.head.merge_comments()
+ } else {
+ d.head.merge_comments_without_examples()
+ }
+ hw.writeln('$comments\n')
+ }
+ if with_toc {
+ hw.writeln('## Contents')
+ }
+ vd.write_markdown_content(d.contents.arr(), mut cw, mut hw, 0, with_toc)
+ footer_text := gen_footer_text(d, !vd.cfg.no_timestamp)
+ cw.writeln('#### $footer_text')
+ return hw.str() + '\n' + cw.str()
+}
+
+fn (vd VDoc) write_markdown_content(contents []doc.DocNode, mut cw strings.Builder, mut hw strings.Builder, indent int, with_toc bool) {
+ for cn in contents {
+ if with_toc && cn.name.len > 0 {
+ hw.writeln(' '.repeat(2 * indent) + '- [${slug(cn.name)}](#$cn.name)')
+ cw.writeln('## $cn.name')
+ }
+ if cn.content.len > 0 {
+ comments := cn.merge_comments_without_examples()
+ cw.writeln('```v\n$cn.content\n```\n$comments\n')
+ // Write examples if any found
+ examples := cn.examples()
+ if vd.cfg.include_examples && examples.len > 0 {
+ example_title := if examples.len > 1 { 'Examples' } else { 'Example' }
+ cw.writeln('$example_title\n```v\n')
+ for example in examples {
+ cw.writeln('$example\n')
+ }
+ cw.writeln('```\n')
+ }
+ cw.writeln(r'[[Return to contents]](#Contents)')
+ cw.writeln('')
+ }
+ vd.write_markdown_content(cn.children, mut cw, mut hw, indent + 1, with_toc)
+ }
+}
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/arrow.svg b/v_windows/v/old/cmd/tools/vdoc/resources/arrow.svg
new file mode 100644
index 0000000..2a0456f
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/arrow.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="dropdown-arrow" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M7 10l5 5 5-5z"/></svg> \ No newline at end of file
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/dark-mode.js b/v_windows/v/old/cmd/tools/vdoc/resources/dark-mode.js
new file mode 100644
index 0000000..075dbb5
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/dark-mode.js
@@ -0,0 +1,6 @@
+(function() {
+ var html = document.getElementsByTagName('html')[0];
+ if (localStorage.getItem('dark-mode') === 'true') {
+ html.classList.add('dark');
+ }
+})();
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/dark.svg b/v_windows/v/old/cmd/tools/vdoc/resources/dark.svg
new file mode 100644
index 0000000..2067d05
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/dark.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="dark-icon" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24"><g><rect fill="none" height="24" width="24"/></g><g><g><g><path d="M11.1,12.08C8.77,7.57,10.6,3.6,11.63,2.01C6.27,2.2,1.98,6.59,1.98,12c0,0.14,0.02,0.28,0.02,0.42 C2.62,12.15,3.29,12,4,12c1.66,0,3.18,0.83,4.1,2.15C9.77,14.63,11,16.17,11,18c0,1.52-0.87,2.83-2.12,3.51 c0.98,0.32,2.03,0.5,3.11,0.5c3.5,0,6.58-1.8,8.37-4.52C18,17.72,13.38,16.52,11.1,12.08z"/></g><path d="M7,16l-0.18,0C6.4,14.84,5.3,14,4,14c-1.66,0-3,1.34-3,3s1.34,3,3,3c0.62,0,2.49,0,3,0c1.1,0,2-0.9,2-2 C9,16.9,8.1,16,7,16z"/></g></g></svg>
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/doc.css b/v_windows/v/old/cmd/tools/vdoc/resources/doc.css
new file mode 100644
index 0000000..a63d357
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/doc.css
@@ -0,0 +1,725 @@
+:root {
+ --background-color: #fff;
+ --link-color: #2779bd;
+ --text-color: #000;
+ --ref-symbol-color: #dae1e7;
+ --ref-symbol-hover-color: #b8c2cc;
+ --title-bottom-line-color: #f1f5f8;
+ --footer-top-line-color: #f1f5f8;
+ --footer-text-color: #616161;
+ --code-signature-border-color: #a0aec0;
+ --menu-background-color: #4b6c88;
+ --menu-text-color: #fff;
+ --menu-indent-line-color: #3b3b3b66;
+ --menu-indent-line-active-color: #00000066;
+ --menu-scrollbar-color: #a0aec0;
+ --menu-toggle-icon-color: #fff;
+ --menu-toggle-icon-hover-color: #00000044;
+ --menu-search-background-color: #00000044;
+ --menu-search-font-color: #fff;
+ --menu-search-result-background-hover-color: #00000021;
+ --menu-search-separator-color: #00000044;
+ --menu-search-title-text-color: #d5efff;
+ --menu-search-badge-background-color: #00000044;
+ --menu-search-badge-background-hover-color: #0000004d;
+ --toc-text-color: #2779bd;
+ --toc-indicator-color: #4299e1;
+ --code-default-text-color: #5c6e74;
+ --code-background-color: #edf2f7;
+ --code-keyword-text-color: #2b6cb0;
+ --code-builtin-text-color: #0a0a0a;
+ --code-function-text-color: #319795;
+ --code-comment-text-color: #93a1a1;
+ --code-punctuation-text-color: #999999;
+ --code-symbol-text-color: #702459;
+ --code-operator-text-color: #a67f59;
+ --attribute-deprecated-background-color: #f59f0b48;
+ --attribute-deprecated-text-color: #92400e;
+ --attribute-text-color: #000000af;
+}
+:root.dark .dark-icon {
+ display: none;
+}
+:root:not(.dark) .light-icon {
+ display: none;
+}
+
+.dark body {
+ --background-color: #1a202c;
+ --text-color: #fff;
+ --link-color: #90cdf4;
+ --ref-symbol-color: #2d3748;
+ --ref-symbol-hover-color: #4a5568;
+ --title-bottom-line-color: #2d3748;
+ --footer-top-line-color: #2d3748;
+ --footer-text-color: #bbd3e1;
+ --code-signature-border-color: #4a5568;
+ --menu-background-color: #2d3748;
+ --menu-text-color: #fff;
+ --menu-indent-line-color: #4a5568;
+ --menu-indent-line-active-color: #90cdf4; /*#4a5568*/
+ --menu-scrollbar-color: #4a5568;
+ --menu-toggle-icon-color: #fff;
+ --menu-search-background-color: #4a5568;
+ --menu-search-font-color: #fff;
+ --menu-search-separator-color: #4a5568;
+ --menu-search-title-text-color: #90cdf4;
+ --menu-search-badge-background-color: #4a5568;
+ --menu-search-badge-background-hover-color: #4a5568;
+ --toc-text-color: #90cdf4;
+ --toc-indicator-color: #4299e1;
+ --code-default-text-color: #cbd5e0;
+ --code-background-color: #2d3748;
+ --code-builtin-text-color: #68d391;
+ --code-keyword-text-color: #63b3ed;
+ --code-function-text-color: #4fd1c5;
+ --code-comment-text-color: #a0aec0;
+ --code-punctuation-text-color: #a0aec0;
+ --code-symbol-text-color: #ed64a6;
+ --attribute-background-color: #ffffff20;
+ --attribute-text-color: #ffffffaf;
+ --attribute-deprecated-text-color: #fef3c7;
+}
+html {
+ height: 100%;
+}
+body {
+ margin: 0;
+ font-family: Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica,
+ Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+ background-color: #fff;
+ background-color: var(--background-color);
+ color: #000;
+ color: var(--text-color);
+ height: 100%;
+}
+#page {
+ height: 100%;
+ padding-top: 56px;
+ box-sizing: border-box;
+ overflow: hidden;
+}
+
+/** Reset for menus */
+.doc-nav ul,
+.doc-toc ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+/* Left nav */
+.doc-nav {
+ position: fixed;
+ width: 100%;
+ left: 0;
+ right: 0;
+ top: 0;
+ display: flex;
+ background-color: #4b6c88;
+ background-color: var(--menu-background-color);
+ color: #fff;
+ color: var(--menu-text-color);
+ flex-direction: column;
+ overflow-y: auto;
+ height: 100vh;
+ z-index: 10;
+ scrollbar-width: thin;
+ scrollbar-color: #a0aec0 transparent;
+ scrollbar-color: var(--menu-scrollbar-color) transparent;
+ font-family: "Work Sans", sans-serif;
+}
+*::-webkit-scrollbar {
+ width: 4px;
+ height: 4px;
+}
+*::-webkit-scrollbar-track {
+ background: transparent;
+}
+*::-webkit-scrollbar-thumb {
+ background-color: #a0aec0;
+ background-color: var(--menu-scrollbar-color);
+ border: 3px solid transparent;
+}
+.doc-nav .content li {
+ line-height: 1.8;
+}
+.doc-nav .content.show {
+ display: flex;
+}
+.doc-nav .content.hidden {
+ display: none;
+}
+.doc-nav #toggle-menu {
+ cursor: pointer;
+ padding: 0.3rem;
+ fill: #fff;
+ fill: var(--menu-toggle-icon-color);
+}
+.doc-nav > .heading-container {
+ position: relative;
+ /* IE11 */
+ position: sticky;
+ position: -webkit-sticky;
+ top: 0;
+ background-color: #4b6c88;
+ background-color: var(--menu-background-color);
+ z-index: 10;
+}
+.doc-nav > .heading-container > .heading {
+ display: flex;
+ flex-direction: column;
+}
+.doc-nav > .heading-container > .heading > .info {
+ display: flex;
+ padding: 0 1rem;
+ height: 56px;
+}
+.doc-nav > .heading-container > .heading > .info > .module {
+ font-size: 1.6rem;
+ font-weight: 500;
+ margin: 0;
+}
+.doc-nav > .heading-container > .heading > .info > .toggle-version-container {
+ display: flex;
+ align-items: center;
+}
+.doc-nav
+ > .heading-container
+ > .heading
+ > .info
+ > .toggle-version-container
+ > #dark-mode-toggle {
+ cursor: pointer;
+ fill: #fff;
+ display: flex;
+ visibility: hidden;
+}
+.doc-nav
+ > .heading-container
+ > .heading
+ > .info
+ > .toggle-version-container
+ > #dark-mode-toggle
+ > svg {
+ width: 1.2rem;
+ height: 1.2rem;
+}
+.doc-nav > .heading-container > .heading > #search {
+ margin: 0.6rem 1.2rem 1rem 1.2rem;
+ border: none;
+ border-radius: 0.2rem;
+ padding: 0.5rem 1rem;
+ outline: none;
+ background-color: #00000044;
+ background-color: var(--menu-search-background-color);
+ color: #fff;
+ color: var(--menu-search-text-color);
+}
+.doc-nav > .heading-container > .heading > #search::placeholder {
+ color: #edf2f7;
+ text-transform: uppercase;
+ font-size: 12px;
+ font-weight: 600;
+}
+.doc-nav > .heading-container > .heading > #search:-ms-input-placeholder {
+ color: #edf2f7;
+ text-transform: uppercase;
+ font-size: 12px;
+ font-weight: 600;
+}
+.doc-nav > .content {
+ padding: 0 2rem 2rem 2rem;
+ display: flex;
+ flex-direction: column;
+}
+.doc-nav > .content > ul > li.active {
+ font-weight: 600;
+}
+.doc-nav > .content > ul > li.open ul {
+ display: initial;
+}
+.doc-nav > .content > ul > li.open > .menu-row > .dropdown-arrow {
+ transform: initial;
+}
+.doc-nav > .content > ul > li > .menu-row {
+ display: flex;
+ align-items: center;
+}
+.doc-nav > .content > ul > li > .menu-row > .dropdown-arrow {
+ transform: rotate(-90deg);
+ height: 18px;
+ width: 18px;
+ margin-left: calc(-18px - 0.3rem);
+ margin-right: 0.3rem;
+ cursor: pointer;
+ fill: #fff;
+ pointer-events: all;
+}
+.doc-nav > .content > ul > li > ul {
+ margin: 0.4rem 0;
+ display: none;
+}
+.doc-nav > .content > ul > li > ul > li {
+ border-color: #ffffff66;
+ border-color: var(--menu-indent-line-color);
+ border-left-width: 1.7px;
+ border-left-style: solid;
+ padding-left: 0.7rem;
+}
+.doc-nav > .content > ul > li > ul > li.active {
+ border-color: #00000066;
+ border-color: var(--menu-search-result-hover-background-color);
+}
+.doc-nav > .content a {
+ color: #fff;
+ color: var(--menu-text-color);
+ text-decoration: none;
+ user-select: none;
+}
+.doc-nav > .content a:hover {
+ text-decoration: underline;
+}
+.doc-nav .search.hidden {
+ display: none;
+}
+.doc-nav .search li {
+ line-height: 1.5;
+}
+.doc-nav > .search .result:hover {
+ background-color: #00000021;
+ background-color: var(--menu-search-result-background-hover-color);
+}
+.doc-nav > .search .result:hover > .link > .definition > .badge {
+ background-color: #0000004d;
+ background-color: var(--menu-search-badge-background-hover-color);
+}
+.doc-nav > .search .result > .link {
+ padding: 0.5rem 1.4rem;
+ text-decoration: none;
+ color: #fff;
+ color: var(--menu-text-color);
+ display: block;
+}
+.doc-nav > .search .result > .link > .definition {
+ display: flex;
+}
+.doc-nav > .search .result > .link > .definition > .title {
+ color: #90cdf4;
+ color: var(--menu-search-title-text-color);
+ font-size: 0.875rem;
+ font-weight: 500;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+.doc-nav > .search .result > .link > .definition > .badge {
+ font-size: 0.75rem;
+ display: inline-flex;
+ padding: 0 0.5rem;
+ background-color: #00000044;
+ background-color: var(--menu-search-badge-background-color);
+ margin-left: auto;
+ align-items: center;
+ border-radius: 9999px;
+ font-weight: 500;
+}
+.doc-nav > .search .result > .link > .description {
+ font-family: Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica,
+ Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+ font-size: 0.75rem;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ margin-top: 0.25rem;
+}
+.doc-nav > .search > hr.separator {
+ margin: 0.5rem 0;
+ border-color: #00000044;
+ border-color: var(--menu-search-separator-color);
+ box-sizing: content-box;
+ height: 0;
+ border-width: 0;
+ border-top-width: 1px;
+ border-style: solid;
+ overflow: visible;
+}
+
+/* Main content */
+.doc-scrollview {
+ height: 100%;
+ overflow-y: scroll;
+}
+.doc-container {
+ display: flex;
+ flex-direction: column-reverse;
+}
+.doc-content {
+ display: flex;
+ flex-direction: column;
+ padding: 1rem;
+ overflow: hidden;
+}
+.doc-content img {
+ width: auto;
+ max-width: 100%;
+}
+.doc-content p {
+ line-height: 1.4;
+}
+.doc-content a {
+ color: #2779bd;
+ color: var(--link-color);
+}
+.doc-content > .doc-node {
+ padding: 5rem 0 2rem 0;
+ margin-top: -4rem;
+ overflow: hidden;
+ word-break: break-all; /* IE11 */
+ word-break: break-word;
+}
+.doc-content > .doc-node.const:nth-child(2) {
+ padding-bottom: 0 !important;
+}
+.doc-content > .doc-node.const:not(:first-child) {
+ padding-top: 4rem;
+}
+.doc-content > .doc-node.const:not(:last-child) {
+ padding-bottom: 2rem;
+}
+.doc-content > .timestamp {
+ font-size: 0.8rem;
+ color: #b8c2cc;
+ color: var(--timestamp-color);
+}
+.doc-content > .doc-node > .title {
+ display: flex;
+ font-family: "Work Sans", sans-serif;
+ font-weight: 500;
+ padding: 0.3rem;
+ align-items: center;
+ margin-bottom: 1rem;
+ border-bottom: 1px solid #f1f5f8;
+ border-bottom: 1px solid var(--title-bottom-line-color);
+}
+.doc-content > .doc-node > .attributes {
+ margin-bottom: 0.6rem;
+}
+.doc-content > .doc-node > .attributes > .attribute {
+ display: inline-block;
+ border-radius: 100px;
+ padding: 0.3rem 0.6rem;
+ background-color: var(--code-background-color);
+ color: var(--attribute-text-color);
+ margin-right: 0.8rem;
+}
+.doc-content > .doc-node > .attributes > .attribute-deprecated {
+ background-color: var(--attribute-deprecated-background-color);
+ color: var(--attribute-deprecated-text-color);
+}
+.doc-content > .doc-node > .title > .link {
+ display: flex;
+ margin-left: auto;
+ fill: #dae1e7;
+ fill: var(--ref-symbol-color);
+}
+.doc-content > .doc-node > .title > .link:hover {
+ fill: #b8c2cc;
+ fill: var(--ref-symbol-hover-color);
+}
+.doc-content > .doc-node h1 {
+ font-size: 2rem;
+}
+.doc-content > .doc-node h2 {
+ font-size: 1.3rem;
+}
+.doc-content > .doc-node .signature {
+ border-color: #a0aec0;
+ border-color: var(--code-signature-border-color);
+ border-left-width: 3px;
+ border-left-style: solid;
+}
+.doc-content > .doc-node > ul > li .task-list-item-checkbox {
+ margin-right: 0.5rem;
+}
+.doc-content > .doc-node > .title h1,
+.doc-content > .doc-node > .title h2,
+.doc-content > .doc-node > .title h3,
+.doc-content > .doc-node > .title h4,
+.doc-content > .doc-node > .title h5,
+.doc-content > .doc-node > .title h6 {
+ font-weight: 500;
+ margin: 0;
+}
+.doc-content > .doc-node > .title h1 a,
+.doc-content > .doc-node > .title h2 a,
+.doc-content > .doc-node > .title h3 a,
+.doc-content > .doc-node > .title h4 a,
+.doc-content > .doc-node > .title h5 a,
+.doc-content > .doc-node > .title h6 a {
+ text-decoration: none;
+ color: #dae1e7;
+ color: var(--ref-symbol-color);
+}
+.doc-content > .doc-node > .title h1 a:hover,
+.doc-content > .doc-node > .title h2 a:hover,
+.doc-content > .doc-node > .title h3 a:hover,
+.doc-content > .doc-node > .title h4 a:hover,
+.doc-content > .doc-node > .title h5 a:hover,
+.doc-content > .doc-node > .title h6 a:hover {
+ color: #b8c2cc;
+ color: var(--ref-symbol-hover-color);
+}
+.doc-content > .footer {
+ padding-top: 1rem;
+ margin-top: auto;
+ bottom: 1rem;
+ color: 616161;
+ color: var(--footer-text-color);
+ border-color: #f1f5f8;
+ border-color: var(--footer-top-line-color);
+ border-top-style: solid;
+ border-top-width: 1px;
+ font-size: 0.8rem;
+ font-weight: 500;
+}
+
+/* Right menu */
+.doc-toc {
+ right: 0;
+ top: 0;
+ height: 100%;
+ overflow-y: auto;
+ padding: 1rem 1rem 0 1rem;
+ width: 100%;
+ box-sizing: border-box;
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+ font-family: "Work Sans", sans-serif;
+}
+.doc-toc::-webkit-scrollbar {
+ display: none;
+}
+.doc-toc li {
+ line-height: 1.5;
+}
+.doc-toc a {
+ color: #2779bd;
+ color: var(--toc-text-color);
+ font-size: 0.9rem;
+ font-weight: 600;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: block;
+ text-decoration: none;
+ border-left-width: 2px;
+ border-left-style: solid;
+ border-color: transparent;
+ padding-left: 0.4rem;
+}
+.doc-toc a:hover {
+ text-decoration: underline;
+}
+.doc-toc a.active {
+ border-color: #4299e1;
+ border-color: var(--toc-indicator-color);
+}
+.doc-toc li ul {
+ margin: 0.2rem 0 0.2rem;
+ font-size: 0.7rem;
+ list-style: none;
+}
+.doc-toc li ul a {
+ font-weight: 400;
+ padding-left: 0.8rem;
+}
+
+/* Code highlight */
+pre,
+code,
+pre code {
+ color: #5c6e74;
+ color: var(--code-default-text-color);
+ font-size: 0.948em;
+ text-shadow: none;
+ font-family: monospace;
+ background-color: #edf2f7;
+ background-color: var(--code-background-color);
+ border-radius: 0.25rem;
+}
+pre code {
+ direction: ltr;
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ line-height: 1.5;
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+ display: block;
+ overflow-x: auto;
+ padding: 1rem;
+}
+code {
+ padding: 0.2rem;
+ vertical-align: middle;
+}
+pre {
+ overflow: auto;
+ margin: 0;
+}
+.namespace {
+ opacity: 0.7;
+}
+.token.comment {
+ color: #93a1a1;
+ color: var(--code-comment-text-color);
+}
+.token.punctuation {
+ color: #999999;
+ color: var(--code-punctuation-text-color);
+}
+.token.number,
+.token.symbol {
+ color: #702459;
+ color: var(--code-symbol-text-color);
+}
+.token.string,
+.token.char,
+.token.builtin {
+ color: #38a169;
+ color: var(--code-builtin-text-color);
+}
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+ color: #a67f59;
+ color: var(--code-operator-text-color);
+ background: transparent;
+}
+.token.boolean,
+.token.keyword {
+ color: #2b6cb0;
+ color: var(--code-keyword-text-color);
+}
+.token.function {
+ color: #319795;
+ color: var(--code-function-text-color);
+}
+
+/* Medium screen and up */
+@media (min-width: 768px) {
+ *::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+ }
+ *::-webkit-scrollbar-thumb {
+ border: 3px solid transparent;
+ }
+ .doc-container {
+ flex-direction: row;
+ }
+ .doc-content {
+ font-size: 0.95rem;
+ flex: 1;
+ padding: 0rem 2rem 1rem 2rem;
+ }
+ .doc-toc {
+ position: relative;
+ /* IE11 */
+ position: sticky;
+ position: -webkit-sticky;
+ align-self: flex-start;
+ top: 56px;
+ height: auto;
+ height: 100vh;
+ min-width: 200px;
+ width: auto;
+ max-width: 300px;
+ }
+ .doc-toc > ul {
+ padding-bottom: 1rem;
+ }
+}
+
+@media (max-width: 1023px) {
+ .doc-nav.hidden {
+ height: auto;
+ }
+ .doc-nav.hidden #search {
+ display: none;
+ }
+ .doc-nav .search.mobile-hidden {
+ display: none;
+ }
+ .doc-nav > .heading-container > .heading > .info {
+ align-items: center;
+ }
+ .doc-nav > .heading-container > .heading > .info > .toggle-version-container {
+ flex-grow: 1;
+ padding: 0 1rem;
+ justify-content: space-between;
+ }
+}
+
+@media (min-width: 1024px) {
+ #page {
+ padding-top: 0;
+ }
+ .doc-nav {
+ width: 300px;
+ }
+ .doc-nav #toggle-menu {
+ display: none;
+ }
+ .doc-nav > .heading-container > .heading > .info {
+ height: auto;
+ padding: 1rem 2rem 0 2rem;
+ flex-direction: column-reverse;
+ justify-content: center;
+ }
+ .doc-nav > .heading-container > .heading > .info > .toggle-version-container {
+ align-items: center;
+ margin-bottom: 0.2rem;
+ display: flex;
+ flex-direction: row-reverse;
+ }
+ .doc-nav
+ > .heading-container
+ > .heading
+ > .info
+ > .toggle-version-container
+ > #dark-mode-toggle {
+ margin-right: auto;
+ }
+ .doc-nav .content.show,
+ .doc-nav .content.hidden {
+ display: flex;
+ }
+ .doc-content > .doc-node.const:nth-child(2) {
+ padding-bottom: 0 !important;
+ }
+ .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-left: 300px;
+ }
+ .doc-node {
+ padding-top: 1rem !important;
+ margin-top: 0 !important;
+ }
+ .doc-toc {
+ top: 0;
+ }
+}
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/doc.js b/v_windows/v/old/cmd/tools/vdoc/resources/doc.js
new file mode 100644
index 0000000..c355d7e
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/doc.js
@@ -0,0 +1,235 @@
+(function () {
+ if (document.body.scrollIntoView) {
+ var docnav = document.querySelector('.doc-nav');
+ var active = docnav.querySelector('li.active');
+ if (active) {
+ active.scrollIntoView({ block: 'center', inline: 'nearest' });
+ }
+ }
+ setupScrollSpy();
+ setupMobileToggle();
+ setupDarkMode();
+ setupSearch();
+ setupCollapse();
+})();
+
+function setupScrollSpy() {
+ var sectionPositions = [];
+ var sections = document.querySelectorAll('section');
+ sections.forEach(function (section) {
+ sectionPositions.push(section.offsetTop);
+ });
+ var scrollPos = 0;
+ window.addEventListener('scroll', function (_) {
+ // Reset classes
+ document.querySelectorAll('.doc-toc a[class="active"]').forEach(function (link) {
+ link.classList.remove('active');
+ });
+ // Set current menu link as active
+ var scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;
+ for (var i = 0; i < sectionPositions.length; i++) {
+ var section = sections[i];
+ var position = sectionPositions[i];
+ if (position >= scrollPosition) {
+ var link = document.querySelector('.doc-toc a[href="#' + section.id + '"]');
+ if (link) {
+ link.classList.add('active');
+ var docToc = document.querySelector('.doc-toc');
+ var tocHeight = docToc.clientHeight;
+ var scrollTop = docToc.scrollTop;
+ if ((document.body.getBoundingClientRect()).top < scrollPos && scrollTop < link.offsetTop - 10) {
+ docToc.scrollTop = link.clientHeight + link.offsetTop - tocHeight + 10;
+ } else if (scrollTop > link.offsetTop - 10) {
+ docToc.scrollTop = link.offsetTop - 10;
+ }
+ }
+ break;
+ }
+ }
+ scrollPos = (document.body.getBoundingClientRect()).top;
+ });
+}
+
+function setupMobileToggle() {
+ var toggle = document.getElementById('toggle-menu');
+ toggle.addEventListener('click', function (_) {
+ var docNav = document.querySelector('.doc-nav');
+ var isHidden = docNav.classList.contains('hidden');
+ docNav.classList.toggle('hidden');
+ var search = document.querySelector('.doc-nav > .search');
+ console.log(search);
+ var searchHasResults = search.classList.contains('has-results');
+ if (isHidden && searchHasResults) {
+ search.classList.remove('mobile-hidden');
+ } else {
+ search.classList.add('mobile-hidden');
+ }
+ var content = document.querySelector('.doc-nav .content');
+ content.classList.toggle('hidden');
+ content.classList.toggle('show');
+ });
+}
+
+function setupDarkMode() {
+ var html = document.getElementsByTagName('html')[0];
+ var darkModeToggle = document.getElementById('dark-mode-toggle');
+ darkModeToggle.addEventListener('click', function () {
+ html.classList.toggle('dark');
+ var isDarkModeEnabled = html.classList.contains('dark');
+ localStorage.setItem('dark-mode', isDarkModeEnabled);
+ darkModeToggle.setAttribute('aria-checked', isDarkModeEnabled)
+ });
+ // Check if css var() is supported and enable dark mode toggle
+ if (window.CSS && CSS.supports('color', 'var(--fake-var)')) {
+ darkModeToggle.style.visibility = 'unset';
+ }
+}
+
+function setupSearch() {
+ var searchInput = document.getElementById('search');
+ var onInputChange = debounce(function (e) {
+ var searchValue = e.target.value.toLowerCase();
+ var menu = document.querySelector('.doc-nav > .content');
+ var search = document.querySelector('.doc-nav > .search');
+ if (searchValue === '') {
+ // reset to default
+ menu.style.display = '';
+ if (!search.classList.contains('hidden')) {
+ search.classList.add('hidden');
+ search.classList.remove('has-results');
+ }
+ } else if (searchValue.length >= 2) {
+ // search for less than 2 characters can display too much results
+ search.innerHTML = '';
+ menu.style.display = 'none';
+ if (search.classList.contains('hidden')) {
+ search.classList.remove('hidden');
+ search.classList.remove('mobile-hidden');
+ search.classList.add('has-results');
+ }
+ // cache length for performance
+ var foundModule = false;
+ var searchModuleIndexLength = searchModuleIndex.length;
+ var ul = document.createElement('ul');
+ search.appendChild(ul);
+ for (var i = 0; i < searchModuleIndexLength; i++) {
+ // no toLowerCase needed because modules are always lowercase
+ var title = searchModuleIndex[i];
+ if (title.indexOf(searchValue) === -1) {
+ continue
+ }
+ foundModule = true;
+ // [description, link]
+ var data = searchModuleData[i];
+ var description = data[0];
+ var link = data[1];
+ var el = createSearchResult({
+ link: link,
+ title: title,
+ description: description,
+ badge: 'module',
+ });
+ ul.appendChild(el);
+ }
+ if (foundModule) {
+ var hr = document.createElement('hr');
+ hr.classList.add('separator');
+ search.appendChild(hr);
+ }
+ var searchIndexLength = searchIndex.length;
+ var results = [];
+ for (var i = 0; i < searchIndexLength; i++) {
+ var title = searchIndex[i];
+ if (title.toLowerCase().indexOf(searchValue) === -1) {
+ continue
+ }
+ // [badge, description, link]
+ var data = searchData[i];
+ var badge = data[0];
+ var description = data[1];
+ var link = data[2];
+ var prefix = data[3];
+ results.push({
+ badge: badge,
+ description: description,
+ link: link,
+ title: prefix + ' ' + title,
+ });
+ }
+ results.sort(function (a, b) {
+ if (a.title < b.title) {
+ return -1;
+ }
+ if (a.title > b.title) {
+ return 1;
+ }
+ return 0;
+ });
+ var ul = document.createElement('ul');
+ search.appendChild(ul);
+ for (var i = 0; i < results.length; i++) {
+ var result = results[i];
+ var el = createSearchResult(result);
+ ul.appendChild(el);
+ }
+ }
+ });
+ searchInput.addEventListener('input', onInputChange);
+}
+
+function createSearchResult(data) {
+ var li = document.createElement('li');
+ li.classList.add('result');
+ var a = document.createElement('a');
+ a.href = data.link;
+ a.classList.add('link');
+ li.appendChild(a);
+ var defintion = document.createElement('div');
+ defintion.classList.add('definition');
+ a.appendChild(defintion);
+ if (data.description) {
+ var description = document.createElement('div');
+ description.classList.add('description');
+ description.textContent = data.description;
+ a.appendChild(description);
+ }
+ var title = document.createElement('span');
+ title.classList.add('title');
+ title.textContent = data.title;
+ defintion.appendChild(title);
+ var badge = document.createElement('badge');
+ badge.classList.add('badge');
+ badge.textContent = data.badge;
+ defintion.appendChild(badge);
+ return li;
+}
+
+function setupCollapse() {
+ var dropdownArrows = document.querySelectorAll('.dropdown-arrow');
+ for (var i = 0; i < dropdownArrows.length; i++) {
+ var dropdownArrow = dropdownArrows[i];
+ dropdownArrow.addEventListener('click', function (e) {
+ var parent = e.target.parentElement.parentElement.parentElement;
+ parent.classList.toggle('open');
+ });
+ }
+}
+
+function debounce(func, timeout) {
+ var timer;
+ return (...args) => {
+ const next = () => func(...args);
+ if (timer) {
+ clearTimeout(timer);
+ }
+ timer = setTimeout(next, timeout > 0 ? timeout : 300);
+ }
+}
+
+document.addEventListener('keypress', (ev) => {
+ if (ev.key == '/') {
+ let search = document.getElementById('search');
+ ev.preventDefault();
+ search.focus();
+ }
+});
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-192x192.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-192x192.png
new file mode 100644
index 0000000..a674500
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-192x192.png
Binary files differ
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-512x512.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-512x512.png
new file mode 100644
index 0000000..fe7294e
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-512x512.png
Binary files differ
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/apple-touch-icon.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/apple-touch-icon.png
new file mode 100644
index 0000000..d2bedd5
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/apple-touch-icon.png
Binary files differ
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/browserconfig.xml b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/browserconfig.xml
new file mode 100644
index 0000000..b3930d0
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/browserconfig.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<browserconfig>
+ <msapplication>
+ <tile>
+ <square150x150logo src="/mstile-150x150.png"/>
+ <TileColor>#da532c</TileColor>
+ </tile>
+ </msapplication>
+</browserconfig>
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-16x16.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-16x16.png
new file mode 100644
index 0000000..ed11964
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-16x16.png
Binary files differ
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-32x32.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-32x32.png
new file mode 100644
index 0000000..083808f
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-32x32.png
Binary files differ
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon.ico b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon.ico
new file mode 100644
index 0000000..5123c5e
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon.ico
Binary files differ
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-144x144.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-144x144.png
new file mode 100644
index 0000000..f34f872
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-144x144.png
Binary files differ
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-150x150.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-150x150.png
new file mode 100644
index 0000000..d511595
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-150x150.png
Binary files differ
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x150.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x150.png
new file mode 100644
index 0000000..ec8e25f
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x150.png
Binary files differ
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x310.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x310.png
new file mode 100644
index 0000000..8b98e30
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x310.png
Binary files differ
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-70x70.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-70x70.png
new file mode 100644
index 0000000..4740338
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-70x70.png
Binary files differ
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/safari-pinned-tab.svg b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/safari-pinned-tab.svg
new file mode 100644
index 0000000..8580c38
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/safari-pinned-tab.svg
@@ -0,0 +1,39 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
+ width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
+ preserveAspectRatio="xMidYMid meet">
+<metadata>
+Created by potrace 1.11, written by Peter Selinger 2001-2013
+</metadata>
+<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
+fill="#000000" stroke="none">
+<path d="M580 6459 c-45 -10 -80 -50 -80 -94 0 -18 20 -89 44 -156 63 -178
+287 -801 371 -1034 62 -172 123 -341 205 -570 39 -110 81 -225 92 -255 19 -52
+62 -173 157 -440 23 -63 44 -122 48 -130 3 -8 7 -19 9 -25 4 -18 354 -989 369
+-1025 8 -19 30 -82 50 -140 20 -58 55 -154 77 -215 22 -60 51 -144 65 -185 14
+-41 29 -79 34 -85 5 -5 9 -17 9 -27 0 -9 7 -32 15 -50 8 -18 53 -139 99 -268
+186 -518 281 -784 326 -910 72 -205 97 -240 200 -289 l55 -25 795 -2 c437 -1
+809 0 826 3 50 8 88 51 118 133 15 41 34 91 41 110 7 19 49 136 94 260 176
+496 213 597 231 645 29 76 472 1309 474 1320 1 6 10 27 19 49 9 21 17 41 17
+45 0 3 32 94 71 201 39 107 72 200 73 205 2 6 6 17 9 25 4 8 38 105 78 215 39
+110 100 281 136 380 76 208 133 366 173 480 29 84 292 814 437 1215 208 573
+204 560 154 611 -26 25 -56 35 -101 31 -6 -1 -102 -10 -185 -17 -27 -3 -70 -7
+-95 -10 -25 -3 -74 -7 -110 -10 -36 -3 -81 -7 -100 -10 -19 -2 -64 -7 -100
+-10 -82 -7 -152 -14 -210 -20 -25 -3 -74 -7 -110 -10 -36 -3 -81 -7 -100 -10
+-19 -2 -64 -7 -100 -10 -147 -13 -262 -24 -315 -30 -16 -2 -64 -6 -105 -10
+-102 -9 -140 -20 -196 -58 -75 -51 -101 -92 -156 -248 -28 -79 -51 -148 -53
+-154 -1 -5 -5 -17 -8 -25 -4 -8 -40 -112 -81 -230 -82 -239 -197 -567 -256
+-735 -21 -60 -59 -171 -85 -245 -26 -74 -64 -184 -85 -245 -22 -60 -89 -254
+-150 -430 -61 -176 -120 -345 -131 -375 -11 -30 -45 -130 -77 -222 -65 -192
+-51 -195 -126 19 -24 71 -72 207 -106 303 -34 96 -81 231 -105 300 -24 69 -71
+204 -105 300 -34 96 -65 186 -70 200 -4 14 -34 97 -65 185 -31 88 -65 185 -75
+215 -17 52 -70 203 -210 600 -97 276 -141 403 -150 430 -77 237 -117 309 -199
+362 -54 33 -91 44 -186 53 -73 6 -130 12 -205 20 -25 3 -74 7 -110 10 -36 3
+-85 8 -110 11 -25 3 -70 7 -100 9 -30 3 -75 7 -100 10 -25 3 -72 7 -105 10
+-33 3 -80 7 -105 10 -25 3 -72 7 -105 10 -33 3 -82 8 -110 10 -27 3 -71 8 -97
+10 -27 3 -72 7 -100 10 -83 9 -137 14 -218 21 -41 3 -77 7 -80 8 -3 2 -21 -1
+-40 -5z"/>
+</g>
+</svg>
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/site.webmanifest b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/site.webmanifest
new file mode 100644
index 0000000..b20abb7
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/site.webmanifest
@@ -0,0 +1,19 @@
+{
+ "name": "",
+ "short_name": "",
+ "icons": [
+ {
+ "src": "/android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "/android-chrome-512x512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ],
+ "theme_color": "#ffffff",
+ "background_color": "#ffffff",
+ "display": "standalone"
+}
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/light.svg b/v_windows/v/old/cmd/tools/vdoc/resources/light.svg
new file mode 100644
index 0000000..21606c9
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/light.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="light-icon" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.79 1.42-1.41zM4 10.5H1v2h3v-2zm9-9.95h-2V3.5h2V.55zm7.45 3.91l-1.41-1.41-1.79 1.79 1.41 1.41 1.79-1.79zm-3.21 13.7l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM20 10.5v2h3v-2h-3zm-8-5c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm-1 16.95h2V19.5h-2v2.95zm-7.45-3.91l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8z"/></svg>
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/link.svg b/v_windows/v/old/cmd/tools/vdoc/resources/link.svg
new file mode 100644
index 0000000..60d93bb
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/link.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></svg>
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/menu.svg b/v_windows/v/old/cmd/tools/vdoc/resources/menu.svg
new file mode 100644
index 0000000..c069b00
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/menu.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" id="toggle-menu" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>
diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/normalize.css b/v_windows/v/old/cmd/tools/vdoc/resources/normalize.css
new file mode 100644
index 0000000..d9fbe67
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/resources/normalize.css
@@ -0,0 +1,171 @@
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
+html {
+ line-height: 1.15;
+ -webkit-text-size-adjust: 100%;
+}
+
+main {
+ display: block;
+}
+
+h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+}
+
+hr {
+ box-sizing: content-box;
+ height: 0;
+ overflow: visible;
+}
+
+pre {
+ font-family: monospace, monospace;
+ font-size: 1em;
+}
+
+a {
+ background-color: transparent;
+}
+
+abbr[title] {
+ border-bottom: none;
+ text-decoration: underline;
+ text-decoration: underline dotted;
+}
+
+b,
+strong {
+ font-weight: bolder;
+}
+
+kbd,
+samp {
+ font-family: monospace, monospace;
+ font-size: 1em;
+}
+
+small {
+ font-size: 80%;
+}
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+sup {
+ top: -0.5em;
+}
+
+img {
+ border-style: none;
+}
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ font-family: inherit;
+ font-size: 100%;
+ line-height: 1.15;
+ margin: 0;
+}
+
+button,
+input {
+ overflow: visible;
+}
+
+button,
+select {
+ text-transform: none;
+}
+
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+ -webkit-appearance: button;
+}
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+ border-style: none;
+ padding: 0;
+}
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+ outline: 1px dotted ButtonText;
+}
+
+fieldset {
+ padding: 0.35em 0.75em 0.625em;
+}
+
+legend {
+ box-sizing: border-box;
+ color: inherit;
+ display: table;
+ max-width: 100%;
+ padding: 0;
+ white-space: normal;
+}
+
+progress {
+ vertical-align: baseline;
+}
+
+textarea {
+ overflow: auto;
+}
+
+[type="checkbox"],
+[type="radio"] {
+ box-sizing: border-box;
+ padding: 0;
+}
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+[type="search"] {
+ -webkit-appearance: textfield;
+ outline-offset: -2px;
+}
+
+[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+::-webkit-file-upload-button {
+ -webkit-appearance: button;
+ font: inherit;
+}
+
+summary {
+ display: list-item;
+}
+
+template {
+ display: none;
+}
+
+[hidden] {
+ display: none;
+}
diff --git a/v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.out b/v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.out
new file mode 100644
index 0000000..41b75a2
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.out
@@ -0,0 +1 @@
+vdoc: No documentation found for /v/vmaster/cmd/tools/vdoc/tests/testdata/project1/main.v
diff --git a/v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.v b/v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.v
new file mode 100644
index 0000000..1a1b527
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.v
@@ -0,0 +1,8 @@
+const (
+ source_root = 'temp'
+)
+
+// funky - comment for function below
+fn funky() {
+ println('hi')
+}
diff --git a/v_windows/v/old/cmd/tools/vdoc/tests/vdoc_file_test.v b/v_windows/v/old/cmd/tools/vdoc/tests/vdoc_file_test.v
new file mode 100644
index 0000000..49a0130
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/tests/vdoc_file_test.v
@@ -0,0 +1,72 @@
+import os
+import rand
+import term
+import v.util.vtest
+import v.util.diff
+
+const vexe = @VEXE
+
+const vroot = @VMODROOT
+
+const diff_cmd = find_diff_cmd()
+
+fn find_diff_cmd() string {
+ return diff.find_working_diff_command() or { '' }
+}
+
+fn test_vet() {
+ os.setenv('VCOLORS', 'never', true)
+ os.chdir(vroot)
+ test_dir := 'cmd/tools/vdoc/tests/testdata'
+ main_files := get_main_files_in_dir(test_dir)
+ fails := check_path(vexe, test_dir, main_files)
+ assert fails == 0
+}
+
+fn get_main_files_in_dir(dir string) []string {
+ mut mfiles := os.walk_ext(dir, '.v')
+ mfiles.sort()
+ return mfiles
+}
+
+fn check_path(vexe string, dir string, tests []string) int {
+ mut nb_fail := 0
+ paths := vtest.filter_vtest_only(tests, basepath: vroot)
+ for path in paths {
+ program := path
+ print(path + ' ')
+ res := os.execute('$vexe doc $program')
+ if res.exit_code < 0 {
+ panic(res.output)
+ }
+ mut expected := os.read_file(program.replace('main.v', 'main.out')) or { panic(err) }
+ expected = clean_line_endings(expected)
+ found := clean_line_endings(res.output)
+ if expected != found {
+ println(term.red('FAIL'))
+ println('============')
+ println('expected:')
+ println(expected)
+ println('============')
+ println('found:')
+ println(found)
+ println('============\n')
+ println('diff:')
+ println(diff.color_compare_strings(diff_cmd, rand.ulid(), found, expected))
+ println('============\n')
+ nb_fail++
+ } else {
+ println(term.green('OK'))
+ }
+ }
+ return nb_fail
+}
+
+fn clean_line_endings(s string) string {
+ mut res := s.trim_space()
+ res = res.replace(' \n', '\n')
+ res = res.replace(' \r\n', '\n')
+ res = res.replace('\r\n', '\n')
+ res = res.trim('\n')
+ return res
+}
diff --git a/v_windows/v/old/cmd/tools/vdoc/utils.v b/v_windows/v/old/cmd/tools/vdoc/utils.v
new file mode 100644
index 0000000..4b07373
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/utils.v
@@ -0,0 +1,275 @@
+module main
+
+import os
+import v.doc
+import term
+import v.ast
+import v.scanner
+import v.token
+import strings
+import v.pref
+
+[inline]
+fn slug(title string) string {
+ return title.replace(' ', '-')
+}
+
+fn escape(str string) string {
+ return str.replace_each(['"', '\\"', '\r\n', '\\n', '\n', '\\n', '\t', '\\t'])
+}
+
+fn get_sym_name(dn doc.DocNode) string {
+ sym_name := if dn.parent_name.len > 0 && dn.parent_name != 'void' {
+ '($dn.parent_name) $dn.name'
+ } else {
+ dn.name
+ }
+ return sym_name
+}
+
+fn get_node_id(dn doc.DocNode) string {
+ tag := if dn.parent_name.len > 0 && dn.parent_name != 'void' {
+ '${dn.parent_name}.$dn.name'
+ } else {
+ dn.name
+ }
+ return slug(tag)
+}
+
+fn is_module_readme(dn doc.DocNode) bool {
+ if dn.comments.len > 0 && dn.content == 'module $dn.name' {
+ return true
+ }
+ return false
+}
+
+fn trim_doc_node_description(description string) string {
+ mut dn_description := description.replace_each(['\r\n', '\n', '"', '\\"'])
+ // 80 is enough to fill one line
+ if dn_description.len > 80 {
+ dn_description = dn_description[..80]
+ }
+ if dn_description.contains('\n') {
+ dn_description = dn_description.split('\n')[0]
+ }
+ // if \ is last character, it ends with \" which leads to a JS error
+ if dn_description.ends_with('\\') {
+ dn_description = dn_description.trim_right('\\')
+ }
+ return dn_description
+}
+
+fn set_output_type_from_str(format string) OutputType {
+ output_type := match format {
+ 'htm', 'html' { OutputType.html }
+ 'md', 'markdown' { OutputType.markdown }
+ 'json' { OutputType.json }
+ 'stdout' { OutputType.stdout }
+ else { OutputType.plaintext }
+ }
+ return output_type
+}
+
+fn get_ignore_paths(path string) ?[]string {
+ ignore_file_path := os.join_path(path, '.vdocignore')
+ ignore_content := os.read_file(ignore_file_path) or {
+ return error_with_code('ignore file not found.', 1)
+ }
+ mut res := []string{}
+ if ignore_content.trim_space().len > 0 {
+ rules := ignore_content.split_into_lines().map(it.trim_space())
+ mut final := []string{}
+ for rule in rules {
+ if rule.contains('*.') || rule.contains('**') {
+ println('vdoc: Wildcards in ignore rules are not allowed for now.')
+ continue
+ }
+ final << rule
+ }
+ res = final.map(os.join_path(path, it.trim_right('/')))
+ } else {
+ mut dirs := os.ls(path) or { return []string{} }
+ res = dirs.map(os.join_path(path, it)).filter(os.is_dir(it))
+ }
+ return res.map(it.replace('/', os.path_separator))
+}
+
+fn is_included(path string, ignore_paths []string) bool {
+ if path.len == 0 {
+ return true
+ }
+ for ignore_path in ignore_paths {
+ if !path.contains(ignore_path) {
+ continue
+ }
+ return false
+ }
+ return true
+}
+
+fn get_modules_list(path string, ignore_paths2 []string) []string {
+ files := os.ls(path) or { return []string{} }
+ mut ignore_paths := get_ignore_paths(path) or { []string{} }
+ ignore_paths << ignore_paths2
+ mut dirs := []string{}
+ for file in files {
+ fpath := os.join_path(path, file)
+ if os.is_dir(fpath) && is_included(fpath, ignore_paths) && !os.is_link(path) {
+ dirs << get_modules_list(fpath, ignore_paths.filter(it.starts_with(fpath)))
+ } else if fpath.ends_with('.v') && !fpath.ends_with('_test.v') {
+ if path in dirs {
+ continue
+ }
+ dirs << path
+ }
+ }
+ dirs.sort()
+ return dirs
+}
+
+fn gen_footer_text(d &doc.Doc, include_timestamp bool) string {
+ footer_text := 'Powered by vdoc.'
+ if !include_timestamp {
+ return footer_text
+ }
+ generated_time := d.time_generated
+ time_str := '$generated_time.day $generated_time.smonth() $generated_time.year $generated_time.hhmmss()'
+ return '$footer_text Generated on: $time_str'
+}
+
+fn color_highlight(code string, tb &ast.Table) string {
+ builtin := ['bool', 'string', 'i8', 'i16', 'int', 'i64', 'i128', 'byte', 'u16', 'u32', 'u64',
+ 'u128', 'rune', 'f32', 'f64', 'int_literal', 'float_literal', 'byteptr', 'voidptr', 'any']
+ highlight_code := fn (tok token.Token, typ HighlightTokenTyp) string {
+ mut lit := ''
+ match typ {
+ .unone, .operator, .punctuation {
+ lit = tok.kind.str()
+ }
+ .string {
+ use_double_quote := tok.lit.contains("'") && !tok.lit.contains('"')
+ unescaped_val := tok.lit.replace('\\\\', '\x01').replace_each(["\\'", "'", '\\"',
+ '"',
+ ])
+ if use_double_quote {
+ s := unescaped_val.replace_each(['\x01', '\\\\', '"', '\\"'])
+ lit = term.yellow('"$s"')
+ } else {
+ s := unescaped_val.replace_each(['\x01', '\\\\', "'", "\\'"])
+ lit = term.yellow("'$s'")
+ }
+ }
+ .char {
+ lit = term.yellow('`$tok.lit`')
+ }
+ .keyword {
+ lit = term.bright_blue(tok.lit)
+ }
+ .builtin, .symbol {
+ lit = term.green(tok.lit)
+ }
+ .function {
+ lit = term.cyan(tok.lit)
+ }
+ .number, .module_ {
+ lit = term.bright_blue(tok.lit)
+ }
+ .boolean {
+ lit = term.bright_magenta(tok.lit)
+ }
+ .none_ {
+ lit = term.red(tok.lit)
+ }
+ .prefix {
+ lit = term.magenta(tok.lit)
+ }
+ else {
+ lit = tok.lit
+ }
+ }
+ return lit
+ }
+ mut s := scanner.new_scanner(code, .parse_comments, &pref.Preferences{ is_fmt: true })
+ mut prev_prev := token.Token{}
+ mut prev := token.Token{}
+ mut tok := s.scan()
+ mut next_tok := s.scan()
+ mut buf := strings.new_builder(200)
+ mut i := 0
+ for i < code.len {
+ if i == tok.pos {
+ mut tok_typ := HighlightTokenTyp.unone
+ match tok.kind {
+ .name {
+ if (tok.lit in builtin || tb.known_type(tok.lit))
+ && (next_tok.kind != .lpar || prev.kind !in [.key_fn, .rpar]) {
+ tok_typ = .builtin
+ } else if
+ next_tok.kind in [.lcbr, .rpar, .eof, .comma, .pipe, .name, .rcbr, .assign, .key_pub, .key_mut, .pipe, .comma]
+ && prev.kind in [.name, .amp, .rsbr, .key_type, .assign, .dot, .question, .rpar, .key_struct, .key_enum, .pipe, .key_interface]
+ && (tok.lit[0].ascii_str().is_upper() || prev_prev.lit in ['C', 'JS']) {
+ tok_typ = .symbol
+ } else if next_tok.kind in [.lpar, .lt] {
+ tok_typ = .function
+ } else if next_tok.kind == .dot {
+ if tok.lit in ['C', 'JS'] {
+ tok_typ = .prefix
+ } else {
+ if tok.lit[0].ascii_str().is_upper() {
+ tok_typ = .symbol
+ } else {
+ tok_typ = .module_
+ }
+ }
+ } else if tok.lit in ['r', 'c'] && next_tok.kind == .string {
+ tok_typ = .prefix
+ } else {
+ tok_typ = .name
+ }
+ }
+ .comment {
+ tok_typ = .comment
+ }
+ .chartoken {
+ tok_typ = .char
+ }
+ .string {
+ tok_typ = .string
+ }
+ .number {
+ tok_typ = .number
+ }
+ .key_true, .key_false {
+ tok_typ = .boolean
+ }
+ .lpar, .lcbr, .rpar, .rcbr, .lsbr, .rsbr, .semicolon, .colon, .comma, .dot {
+ tok_typ = .punctuation
+ }
+ .key_none {
+ tok_typ = .none_
+ }
+ else {
+ if token.is_key(tok.lit) || token.is_decl(tok.kind) {
+ tok_typ = .keyword
+ } else if tok.kind == .decl_assign || tok.kind.is_assign() || tok.is_unary()
+ || tok.kind.is_relational() || tok.kind.is_infix() {
+ tok_typ = .operator
+ }
+ }
+ }
+ buf.write_string(highlight_code(tok, tok_typ))
+ if prev_prev.kind == .eof || prev.kind == .eof || next_tok.kind == .eof {
+ break
+ }
+ prev_prev = prev
+ prev = tok
+ i = tok.pos + tok.len
+ tok = next_tok
+ next_tok = s.scan()
+ } else {
+ buf.write_b(code[i])
+ i++
+ }
+ }
+ return buf.str()
+}
diff --git a/v_windows/v/old/cmd/tools/vdoc/vdoc.v b/v_windows/v/old/cmd/tools/vdoc/vdoc.v
new file mode 100644
index 0000000..f4f0264
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vdoc/vdoc.v
@@ -0,0 +1,511 @@
+module main
+
+import markdown
+import os
+import os.cmdline
+import time
+import strings
+import sync
+import runtime
+import v.doc
+import v.pref
+import v.vmod
+import json
+import term
+
+const (
+ allowed_formats = ['md', 'markdown', 'json', 'text', 'stdout', 'html', 'htm']
+ vexe = pref.vexe_path()
+ vroot = os.dir(vexe)
+ tabs = ['\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t']
+)
+
+enum OutputType {
+ unset
+ html
+ markdown
+ json
+ plaintext
+ stdout
+}
+
+struct VDoc {
+ cfg Config [required]
+mut:
+ docs []doc.Doc
+ assets map[string]string
+ manifest vmod.Manifest
+ search_index []string
+ search_data []SearchResult
+ search_module_index []string // search results are split into a module part and the rest
+ search_module_data []SearchModuleResult
+}
+
+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
+ inline_assets bool
+ no_timestamp bool
+ output_path string
+ output_type OutputType = .unset
+ input_path string
+ symbol_name string
+ platform doc.Platform
+}
+
+//
+struct Output {
+mut:
+ path string
+ typ OutputType = .unset
+}
+
+struct ParallelDoc {
+ d doc.Doc
+ out Output
+}
+
+fn (vd VDoc) gen_json(d doc.Doc) string {
+ cfg := vd.cfg
+ mut jw := strings.new_builder(200)
+ comments := if cfg.include_examples {
+ d.head.merge_comments()
+ } else {
+ d.head.merge_comments_without_examples()
+ }
+ jw.write_string('{"module_name":"$d.head.name","description":"${escape(comments)}","contents":')
+ jw.write_string(json.encode(d.contents.keys().map(d.contents[it])))
+ jw.write_string(',"generator":"vdoc","time_generated":"$d.time_generated.str()"}')
+ return jw.str()
+}
+
+fn (vd VDoc) gen_plaintext(d doc.Doc) string {
+ cfg := vd.cfg
+ mut pw := strings.new_builder(200)
+ if cfg.is_color {
+ content_arr := d.head.content.split(' ')
+ pw.writeln('${term.bright_blue(content_arr[0])} ${term.green(content_arr[1])}\n')
+ } else {
+ pw.writeln('$d.head.content\n')
+ }
+ comments := if cfg.include_examples {
+ d.head.merge_comments()
+ } else {
+ d.head.merge_comments_without_examples()
+ }
+ if comments.trim_space().len > 0 && !cfg.pub_only {
+ pw.writeln(comments.split_into_lines().map(' ' + it).join('\n'))
+ }
+ vd.write_plaintext_content(d.contents.arr(), mut pw)
+ return pw.str()
+}
+
+fn (vd VDoc) write_plaintext_content(contents []doc.DocNode, mut pw strings.Builder) {
+ cfg := vd.cfg
+ for cn in contents {
+ if cn.content.len > 0 {
+ if cfg.is_color {
+ pw.writeln(color_highlight(cn.content, vd.docs[0].table))
+ } else {
+ pw.writeln(cn.content)
+ }
+ if cn.comments.len > 0 && !cfg.pub_only {
+ comments := if cfg.include_examples {
+ cn.merge_comments()
+ } else {
+ cn.merge_comments_without_examples()
+ }
+ pw.writeln(comments.trim_space().split_into_lines().map(' ' + it).join('\n'))
+ }
+ if cfg.show_loc {
+ pw.writeln('Location: $cn.file_path:${cn.pos.line_nr + 1}\n')
+ }
+ }
+ vd.write_plaintext_content(cn.children, mut pw)
+ }
+}
+
+fn (vd VDoc) render_doc(d doc.Doc, out Output) (string, string) {
+ name := vd.get_file_name(d.head.name, out)
+ output := match out.typ {
+ .html { vd.gen_html(d) }
+ .markdown { vd.gen_markdown(d, true) }
+ .json { vd.gen_json(d) }
+ else { vd.gen_plaintext(d) }
+ }
+ return name, output
+}
+
+// get_file_name returns the final file name from a module name
+fn (vd VDoc) get_file_name(mod string, out Output) string {
+ cfg := vd.cfg
+ mut name := mod
+ // since builtin is generated first, ignore it
+ if (cfg.is_vlib && mod == 'builtin' && !cfg.include_readme) || mod == 'README' {
+ name = 'index'
+ } else if !cfg.is_multi && !os.is_dir(out.path) {
+ name = os.file_name(out.path)
+ }
+ name = name + match out.typ {
+ .html { '.html' }
+ .markdown { '.md' }
+ .json { '.json' }
+ else { '.txt' }
+ }
+ return name
+}
+
+fn (vd VDoc) work_processor(mut work sync.Channel, mut wg sync.WaitGroup) {
+ for {
+ mut pdoc := ParallelDoc{}
+ if !work.pop(&pdoc) {
+ break
+ }
+ file_name, content := vd.render_doc(pdoc.d, pdoc.out)
+ output_path := os.join_path(pdoc.out.path, file_name)
+ println('Generating $pdoc.out.typ in "$output_path"')
+ os.write_file(output_path, content) or { panic(err) }
+ }
+ wg.done()
+}
+
+fn (vd VDoc) render_parallel(out Output) {
+ vjobs := runtime.nr_jobs()
+ mut work := sync.new_channel<ParallelDoc>(u32(vd.docs.len))
+ mut wg := sync.new_waitgroup()
+ for i in 0 .. vd.docs.len {
+ p_doc := ParallelDoc{vd.docs[i], out}
+ work.push(&p_doc)
+ }
+ work.close()
+ wg.add(vjobs)
+ for _ in 0 .. vjobs {
+ go vd.work_processor(mut work, mut wg)
+ }
+ wg.wait()
+}
+
+fn (vd VDoc) render(out Output) map[string]string {
+ mut docs := map[string]string{}
+ for doc in vd.docs {
+ name, output := vd.render_doc(doc, out)
+ docs[name] = output.trim_space()
+ }
+ vd.vprintln('Rendered: ' + docs.keys().str())
+ return docs
+}
+
+fn (vd VDoc) get_readme(path string) string {
+ mut fname := ''
+ for name in ['readme', 'README'] {
+ if os.exists(os.join_path(path, '${name}.md')) {
+ fname = name
+ break
+ }
+ }
+ if fname == '' {
+ return ''
+ }
+ readme_path := os.join_path(path, '${fname}.md')
+ vd.vprintln('Reading README file from $readme_path')
+ readme_contents := os.read_file(readme_path) or { '' }
+ return readme_contents
+}
+
+fn (vd VDoc) emit_generate_err(err IError) {
+ cfg := vd.cfg
+ mut err_msg := err.msg
+ if err.code == 1 {
+ mod_list := get_modules_list(cfg.input_path, []string{})
+ println('Available modules:\n==================')
+ for mod in mod_list {
+ println(mod.all_after('vlib/').all_after('modules/').replace('/', '.'))
+ }
+ err_msg += ' Use the `-m` flag when generating docs from a directory that has multiple modules.'
+ }
+ eprintln(err_msg)
+}
+
+fn (mut vd VDoc) generate_docs_from_file() {
+ cfg := vd.cfg
+ mut out := Output{
+ path: cfg.output_path
+ typ: cfg.output_type
+ }
+ if out.path.len == 0 {
+ if cfg.output_type == .unset {
+ out.typ = .stdout
+ } else {
+ vd.vprintln('No output path has detected. Using input path instead.')
+ out.path = cfg.input_path
+ }
+ } else if out.typ == .unset {
+ vd.vprintln('Output path detected. Identifying output type..')
+ ext := os.file_ext(out.path)
+ out.typ = set_output_type_from_str(ext.all_after('.'))
+ }
+ if cfg.include_readme && out.typ !in [.html, .stdout] {
+ eprintln('vdoc: Including README.md for doc generation is supported on HTML output, or when running directly in the terminal.')
+ exit(1)
+ }
+ dir_path := if cfg.is_vlib {
+ vroot
+ } else if os.is_dir(cfg.input_path) {
+ cfg.input_path
+ } else {
+ os.dir(cfg.input_path)
+ }
+ manifest_path := os.join_path(dir_path, 'v.mod')
+ if os.exists(manifest_path) {
+ vd.vprintln('Reading v.mod info from $manifest_path')
+ if manifest := vmod.from_file(manifest_path) {
+ vd.manifest = manifest
+ }
+ }
+ if cfg.include_readme {
+ readme_contents := vd.get_readme(dir_path)
+ comment := doc.DocComment{
+ text: readme_contents
+ }
+ if out.typ == .stdout {
+ println(markdown.to_plain(readme_contents))
+ } else if out.typ == .html && cfg.is_multi {
+ vd.docs << doc.Doc{
+ head: doc.DocNode{
+ name: 'README'
+ comments: [comment]
+ }
+ time_generated: time.now()
+ }
+ }
+ }
+ dirs := if cfg.is_multi {
+ get_modules_list(cfg.input_path, []string{})
+ } else {
+ [cfg.input_path]
+ }
+ for dirpath in dirs {
+ vd.vprintln('Generating $out.typ docs for "$dirpath"')
+ mut dcs := doc.generate(dirpath, cfg.pub_only, true, cfg.platform, cfg.symbol_name) or {
+ vd.emit_generate_err(err)
+ exit(1)
+ }
+ if dcs.contents.len == 0 {
+ continue
+ }
+ if cfg.is_multi || (!cfg.is_multi && cfg.include_readme) {
+ readme_contents := vd.get_readme(dirpath)
+ comment := doc.DocComment{
+ text: readme_contents
+ }
+ dcs.head.comments = [comment]
+ }
+ if cfg.pub_only {
+ for name, dc in dcs.contents {
+ dcs.contents[name].content = dc.content.all_after('pub ')
+ for i, cc in dc.children {
+ dcs.contents[name].children[i].content = cc.content.all_after('pub ')
+ }
+ }
+ }
+ vd.docs << dcs
+ }
+ // Important. Let builtin be in the top of the module list
+ // if we are generating docs for vlib.
+ if cfg.is_vlib {
+ mut docs := vd.docs.filter(it.head.name == 'builtin')
+ docs << vd.docs.filter(it.head.name != 'builtin')
+ vd.docs = docs
+ }
+ vd.vprintln('Rendering docs...')
+ if out.path.len == 0 || out.path == 'stdout' {
+ if out.typ == .html {
+ vd.render_static_html(out)
+ }
+ outputs := vd.render(out)
+ if outputs.len == 0 {
+ eprintln('vdoc: No documentation found for ${dirs[0]}')
+ exit(1)
+ } else {
+ first := outputs.keys()[0]
+ println(outputs[first])
+ }
+ } else {
+ if !os.exists(out.path) {
+ os.mkdir_all(out.path) or { panic(err) }
+ } else if !os.is_dir(out.path) {
+ out.path = os.real_path('.')
+ }
+ if cfg.is_multi {
+ out.path = os.join_path(out.path, '_docs')
+ if !os.exists(out.path) {
+ os.mkdir(out.path) or { panic(err) }
+ } else {
+ for fname in css_js_assets {
+ existing_asset_path := os.join_path(out.path, fname)
+ if os.exists(existing_asset_path) {
+ os.rm(existing_asset_path) or { panic(err) }
+ }
+ }
+ }
+ }
+ if out.typ == .html {
+ vd.render_static_html(out)
+ }
+ vd.render_parallel(out)
+ if out.typ == .html {
+ println('Creating search index...')
+ vd.collect_search_index(out)
+ vd.render_search_index(out)
+ // move favicons to target directory
+ println('Copying favicons...')
+ favicons := os.ls(favicons_path) or { panic(err) }
+ for favicon in favicons {
+ favicon_path := os.join_path(favicons_path, favicon)
+ destination_path := os.join_path(out.path, favicon)
+ os.cp(favicon_path, destination_path) or { panic(err) }
+ }
+ }
+ }
+}
+
+fn (vd VDoc) vprintln(str string) {
+ if vd.cfg.is_verbose {
+ println('vdoc: $str')
+ }
+}
+
+fn parse_arguments(args []string) Config {
+ mut cfg := Config{}
+ cfg.is_color = term.can_show_color_on_stdout()
+ for i := 0; i < args.len; i++ {
+ arg := args[i]
+ current_args := args[i..]
+ match arg {
+ '-all' {
+ cfg.pub_only = false
+ }
+ '-f' {
+ format := cmdline.option(current_args, '-f', '')
+ if format !in allowed_formats {
+ allowed_str := allowed_formats.join(', ')
+ eprintln('vdoc: "$format" is not a valid format. Only $allowed_str are allowed.')
+ exit(1)
+ }
+ cfg.output_type = set_output_type_from_str(format)
+ i++
+ }
+ '-color' {
+ cfg.is_color = true
+ }
+ '-no-color' {
+ cfg.is_color = false
+ }
+ '-inline-assets' {
+ cfg.inline_assets = true
+ }
+ '-l' {
+ cfg.show_loc = true
+ }
+ '-m' {
+ cfg.is_multi = true
+ }
+ '-o' {
+ opath := cmdline.option(current_args, '-o', '')
+ cfg.output_path = if opath == 'stdout' { opath } else { os.real_path(opath) }
+ i++
+ }
+ '-os' {
+ platform_str := cmdline.option(current_args, '-os', '')
+ if platform_str == 'cross' {
+ eprintln('`v doc -os cross` is not supported yet.')
+ exit(1)
+ }
+ selected_platform := doc.platform_from_string(platform_str) or {
+ eprintln(err.msg)
+ exit(1)
+ }
+ cfg.platform = selected_platform
+ i++
+ }
+ '-no-timestamp' {
+ cfg.no_timestamp = true
+ }
+ '-no-examples' {
+ cfg.include_examples = false
+ }
+ '-readme' {
+ cfg.include_readme = true
+ }
+ '-v' {
+ cfg.is_verbose = true
+ }
+ else {
+ if cfg.input_path.len < 1 {
+ cfg.input_path = arg
+ } else if !cfg.is_multi {
+ // Symbol name filtering should not be enabled
+ // in multi-module documentation mode.
+ cfg.symbol_name = arg
+ }
+ if i == args.len - 1 {
+ break
+ }
+ }
+ }
+ }
+ // Correct from configuration from user input
+ if cfg.output_path == 'stdout' && cfg.output_type == .html {
+ cfg.inline_assets = true
+ }
+ $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.trim_right('/') == 'vlib' {
+ cfg.is_vlib = true
+ cfg.is_multi = true
+ cfg.input_path = os.join_path(vroot, 'vlib')
+ } 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 {
+ eprintln('vdoc: $err')
+ exit(1)
+ }
+ cfg.input_path = mod_path
+ }
+ return cfg
+}
+
+fn main() {
+ if os.args.len < 2 || '-h' in os.args || '-help' in os.args || '--help' in os.args
+ || os.args[1..] == ['doc', 'help'] {
+ os.system('$vexe help doc')
+ exit(0)
+ }
+ args := os.args[2..].clone()
+ cfg := parse_arguments(args)
+ if cfg.input_path.len == 0 {
+ eprintln('vdoc: No input path found.')
+ exit(1)
+ }
+ // Config is immutable from this point on
+ mut vd := VDoc{
+ cfg: cfg
+ manifest: vmod.Manifest{
+ repo_url: ''
+ }
+ }
+ vd.vprintln('Setting output type to "$cfg.output_type"')
+ vd.generate_docs_from_file()
+}