From f5c4671bfbad96bf346bd7e9a21fc4317b4959df Mon Sep 17 00:00:00 2001 From: Indrajith K L Date: Sat, 3 Dec 2022 17:00:20 +0530 Subject: Adds most of the tools --- v_windows/v/old/cmd/tools/vdoc/html.v | 553 ++++++++++++++++ .../v/old/cmd/tools/vdoc/html_tag_escape_test.v | 6 + v_windows/v/old/cmd/tools/vdoc/markdown.v | 55 ++ v_windows/v/old/cmd/tools/vdoc/resources/arrow.svg | 1 + .../v/old/cmd/tools/vdoc/resources/dark-mode.js | 6 + v_windows/v/old/cmd/tools/vdoc/resources/dark.svg | 1 + v_windows/v/old/cmd/tools/vdoc/resources/doc.css | 725 +++++++++++++++++++++ v_windows/v/old/cmd/tools/vdoc/resources/doc.js | 235 +++++++ .../resources/favicons/android-chrome-192x192.png | Bin 0 -> 6083 bytes .../resources/favicons/android-chrome-512x512.png | Bin 0 -> 18209 bytes .../vdoc/resources/favicons/apple-touch-icon.png | Bin 0 -> 5707 bytes .../vdoc/resources/favicons/browserconfig.xml | 9 + .../vdoc/resources/favicons/favicon-16x16.png | Bin 0 -> 853 bytes .../vdoc/resources/favicons/favicon-32x32.png | Bin 0 -> 1305 bytes .../cmd/tools/vdoc/resources/favicons/favicon.ico | Bin 0 -> 15086 bytes .../vdoc/resources/favicons/mstile-144x144.png | Bin 0 -> 4512 bytes .../vdoc/resources/favicons/mstile-150x150.png | Bin 0 -> 4360 bytes .../vdoc/resources/favicons/mstile-310x150.png | Bin 0 -> 4927 bytes .../vdoc/resources/favicons/mstile-310x310.png | Bin 0 -> 10195 bytes .../tools/vdoc/resources/favicons/mstile-70x70.png | Bin 0 -> 3093 bytes .../vdoc/resources/favicons/safari-pinned-tab.svg | 39 ++ .../tools/vdoc/resources/favicons/site.webmanifest | 19 + v_windows/v/old/cmd/tools/vdoc/resources/light.svg | 1 + v_windows/v/old/cmd/tools/vdoc/resources/link.svg | 1 + v_windows/v/old/cmd/tools/vdoc/resources/menu.svg | 1 + .../v/old/cmd/tools/vdoc/resources/normalize.css | 171 +++++ .../tools/vdoc/tests/testdata/project1/main.out | 1 + .../cmd/tools/vdoc/tests/testdata/project1/main.v | 8 + .../v/old/cmd/tools/vdoc/tests/vdoc_file_test.v | 72 ++ v_windows/v/old/cmd/tools/vdoc/utils.v | 275 ++++++++ v_windows/v/old/cmd/tools/vdoc/vdoc.v | 511 +++++++++++++++ 31 files changed, 2690 insertions(+) create mode 100644 v_windows/v/old/cmd/tools/vdoc/html.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/html_tag_escape_test.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/markdown.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/arrow.svg create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/dark-mode.js create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/dark.svg create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/doc.css create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/doc.js create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-192x192.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-512x512.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/apple-touch-icon.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/browserconfig.xml create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-16x16.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-32x32.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon.ico create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-144x144.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-150x150.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x150.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x310.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-70x70.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/safari-pinned-tab.svg create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/site.webmanifest create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/light.svg create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/link.svg create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/menu.svg create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/normalize.css create mode 100644 v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.out create mode 100644 v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/tests/vdoc_file_test.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/utils.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/vdoc.v (limited to 'v_windows/v/old/cmd/tools/vdoc') 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 = '' + html_content = ' + + + + + + {{ title }} | vdoc + + + + + + + + + + + {{ head_assets }} + + +
+ +
+
+
+{{ contents }} + +
+ {{ right_content }} +
+
+
+ {{ footer_assets }} + + +' +) + +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('
  • ') + for j, cdoc in submodules { + if j == 0 { + modules_toc.write_string('') + } + } + modules_toc.write_string('
  • ') + } + } + 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]}\n${tabs[0]}\n${tabs[0]}' + } else { + '\n${tabs[0]}\n${tabs[0]}\n${tabs[0]}' + }).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' { + '
    ' + } else { + '' + }).replace('{{ footer_content }}', gen_footer_text(d, !cfg.no_timestamp)).replace('{{ footer_assets }}', + if cfg.inline_assets { + '' + } else { + '' + }) + 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 '$lit' + } + 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 { ' #' } else { '' } + if head && is_module_readme(dn) { + node_id = 'readme_$node_id' + hash_link = ' #' + } + dnw.writeln('${tabs[1]}
    ') + if dn.name.len > 0 { + if dn.kind == .const_group { + dnw.write_string('${tabs[2]}
    <$head_tag>$sym_name$hash_link') + } else { + dnw.write_string('${tabs[2]}
    <$head_tag>$dn.kind $sym_name$hash_link') + } + if link.len != 0 { + dnw.write_string('$link_svg') + } + dnw.write_string('
    ') + } + if tags.len > 0 || has_deprecated { + mut attributes := if has_deprecated { + '
    deprecated
    ' + } else { + '' + } + attributes += tags.map('
    $it
    ').join('') + dnw.writeln('
    $attributes
    ') + } + if !head && dn.content.len > 0 { + dnw.writeln('
    $highlighted_code
    ') + } + // 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('

    $example_title

    ') + for example in examples { + // hl_example := html_highlight(example, tb) + dnw.writeln('
    $example
    ') + } + dnw.writeln('
    ') + } + dnw.writeln('
    ') + dnw_str := dnw.str() + return dnw_str +} + +fn html_tag_escape(str string) string { + excaped_string := str.replace_each(['<', '<', '>', '>']) + mut re := regex.regex_opt(r'`.+[(<)(>)].+`') 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('
  • README') + } else if dn.name != 'Constants' { + toc.write_string('
  • $dn.kind $dn.name') + toc.writeln(' ') + } else { + toc.write_string('
  • $dn.name') + } + toc.writeln('
  • ') +} 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>' + assert html_tag_escape('``') == '``' +} 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(['', '`']) +} + +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 @@ + \ 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 @@ + 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 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-192x192.png 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 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-512x512.png 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 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/apple-touch-icon.png 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 @@ + + + + + + #da532c + + + 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 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-16x16.png 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 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-32x32.png 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 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon.ico 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 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-144x144.png 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 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-150x150.png 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 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x150.png 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 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x310.png 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 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-70x70.png 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 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + 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 @@ + 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 @@ + 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 @@ + 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(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() +} -- cgit v1.2.3