From b0a2c28c19acd4595c622787409d227e42bf7fd5 Mon Sep 17 00:00:00 2001
From: Henrixounez <30901439+Henrixounez@users.noreply.github.com>
Date: Wed, 23 Sep 2020 20:50:51 +0200
Subject: [PATCH] vdoc: rendering optimizations (#6457)
---
cmd/tools/vdoc.v | 137 ++++++++++++++++++++++++++++++++---------------
1 file changed, 93 insertions(+), 44 deletions(-)
diff --git a/cmd/tools/vdoc.v b/cmd/tools/vdoc.v
index c337ecc817..e4b51efcab 100644
--- a/cmd/tools/vdoc.v
+++ b/cmd/tools/vdoc.v
@@ -7,6 +7,8 @@ import os
import os.cmdline
import time
import strings
+import sync
+import runtime
import v.doc
import v.scanner
import v.table
@@ -113,6 +115,12 @@ mut:
output_type OutputType = .unset
docs []doc.Doc
manifest vmod.Manifest
+ assets map[string]string
+}
+
+struct ParallelDoc {
+ d doc.Doc
+ i int
}
fn slug(title string) string {
@@ -132,6 +140,7 @@ fn open_url(url string) {
}
fn (mut cfg DocConfig) serve_html() {
+ cfg.render_static()
docs := cfg.render()
dkeys := docs.keys()
if dkeys.len < 1 {
@@ -460,14 +469,6 @@ fn (cfg DocConfig) gen_html(idx int) string {
}
write_toc(cn, dcs.contents, mut toc)
} // write head
- // get resources
- doc_css := cfg.get_resource(css_js_assets[0], true)
- normalize_css := cfg.get_resource(css_js_assets[1], true)
- doc_js := cfg.get_resource(css_js_assets[2], !cfg.serve_http)
- light_icon := cfg.get_resource('light.svg', true)
- dark_icon := cfg.get_resource('dark.svg', true)
- menu_icon := cfg.get_resource('menu.svg', true)
- arrow_icon := cfg.get_resource('arrow.svg', true)
// write css
version := if cfg.manifest.version.len != 0 { cfg.manifest.version } else { '' }
header_name := if cfg.is_multi && cfg.docs.len > 1 { os.file_name(os.real_path(cfg.input_path)) } else { dcs.head.name }
@@ -490,7 +491,7 @@ fn (cfg DocConfig) gen_html(idx int) string {
'./' + doc.head.name + '.html'
}
submodules := cfg.docs.filter(it.head.name.starts_with(submod_prefix + '.'))
- dropdown := if submodules.len > 0 { arrow_icon } else { '' }
+ dropdown := if submodules.len > 0 { cfg.assets['arrow_icon'] } else { '' }
mut is_submodule_open := false
for _, cdoc in submodules {
if cdoc.head.name == dcs.head.name {
@@ -514,11 +515,11 @@ fn (cfg DocConfig) gen_html(idx int) string {
}
}
return html_content.replace('{{ title }}', dcs.head.name).replace('{{ head_name }}',
- header_name).replace('{{ version }}', version).replace('{{ light_icon }}', light_icon).replace('{{ dark_icon }}',
- dark_icon).replace('{{ menu_icon }}', menu_icon).replace('{{ head_assets }}', if cfg.inline_assets {
- '\n \n '
+ header_name).replace('{{ version }}', version).replace('{{ light_icon }}', cfg.assets['light_icon']).replace('{{ dark_icon }}',
+ cfg.assets['dark_icon']).replace('{{ menu_icon }}', cfg.assets['menu_icon']).replace('{{ head_assets }}', if cfg.inline_assets {
+ '\n \n '
} else {
- '\n \n '
+ '\n \n '
}).replace('{{ toc_links }}', if cfg.is_multi || cfg.docs.len > 1 {
toc2.str()
} else {
@@ -530,9 +531,9 @@ fn (cfg DocConfig) gen_html(idx int) string {
''
}).replace('{{ footer_content }}', 'Powered by vdoc. Generated on: $time_gen').replace('{{ footer_assets }}',
if cfg.inline_assets {
- ''
+ ''
} else {
- ''
+ ''
})
}
@@ -577,37 +578,88 @@ fn (cfg DocConfig) gen_markdown(idx int, with_toc bool) string {
return hw.str() + '\n' + cw.str()
}
+fn (cfg DocConfig) render_doc(doc doc.Doc, i int) (string, string) {
+ // since builtin is generated first, ignore it
+ mut name := if ('vlib' in cfg.input_path &&
+ doc.head.name == 'builtin' && !cfg.include_readme) ||
+ doc.head.name == 'README' {
+ 'index'
+ } else if !cfg.is_multi && !os.is_dir(cfg.output_path) {
+ os.file_name(cfg.output_path)
+ } else {
+ doc.head.name
+ }
+ name = name + match cfg.output_type {
+ .html { '.html' }
+ .markdown { '.md' }
+ .json { '.json' }
+ else { '.txt' }
+ }
+ output := match cfg.output_type {
+ .html { cfg.gen_html(i) }
+ .markdown { cfg.gen_markdown(i, true) }
+ .json { cfg.gen_json(i) }
+ else { cfg.gen_plaintext(i) }
+ }
+ return name, output
+}
+
+fn (cfg DocConfig)work_processor(mut work sync.Channel, mut wg sync.WaitGroup) {
+ for {
+ mut pdoc := ParallelDoc{}
+ if !work.pop(&pdoc) {
+ break
+ }
+ file_name, content := cfg.render_doc(pdoc.d, pdoc.i)
+ output_path := os.join_path(cfg.output_path, file_name)
+ println('Generating ${output_path}...')
+ os.write_file(output_path, content)
+ }
+ wg.done()
+}
+
+fn (cfg DocConfig) render_parallel() {
+ vjobs := runtime.nr_jobs()
+ mut work := sync.new_channel(cfg.docs.len)
+ mut wg := sync.new_waitgroup()
+
+ for i in 0 .. cfg.docs.len {
+ p_doc := ParallelDoc{cfg.docs[i], i}
+ work.push(&p_doc)
+ }
+ work.close()
+ wg.add(vjobs)
+ for _ in 0 .. vjobs {
+ go cfg.work_processor(mut work, mut wg)
+ }
+ wg.wait()
+}
+
fn (cfg DocConfig) render() map[string]string {
mut docs := map[string]string{}
+
for i, doc in cfg.docs {
- // since builtin is generated first, ignore it
- mut name := if ('vlib' in cfg.input_path &&
- doc.head.name == 'builtin' && !cfg.include_readme) ||
- doc.head.name == 'README' {
- 'index'
- } else if !cfg.is_multi && !os.is_dir(cfg.output_path) {
- os.file_name(cfg.output_path)
- } else {
- doc.head.name
- }
- name = name + match cfg.output_type {
- .html { '.html' }
- .markdown { '.md' }
- .json { '.json' }
- else { '.txt' }
- }
- output := match cfg.output_type {
- .html { cfg.gen_html(i) }
- .markdown { cfg.gen_markdown(i, true) }
- .json { cfg.gen_json(i) }
- else { cfg.gen_plaintext(i) }
- }
+ name, output := cfg.render_doc(doc, i)
docs[name] = output.trim_space()
}
cfg.vprintln('Rendered: ' + docs.keys().str())
return docs
}
+fn (mut cfg DocConfig) render_static() {
+ if cfg.output_type == .html {
+ cfg.assets = {
+ 'doc_css': cfg.get_resource(css_js_assets[0], true),
+ 'normalize_css': cfg.get_resource(css_js_assets[1], true),
+ 'doc_js': cfg.get_resource(css_js_assets[2], !cfg.serve_http),
+ 'light_icon': cfg.get_resource('light.svg', true),
+ 'dark_icon': cfg.get_resource('dark.svg', true),
+ 'menu_icon': cfg.get_resource('menu.svg', true),
+ 'arrow_icon': cfg.get_resource('arrow.svg', true)
+ }
+ }
+}
+
fn (cfg DocConfig) get_readme(path string) string {
mut fname := ''
for name in ['readme', 'README'] {
@@ -748,6 +800,7 @@ fn (mut cfg DocConfig) generate_docs_from_file() {
}
cfg.vprintln('Rendering docs...')
if cfg.output_path.len == 0 || cfg.output_path == 'stdout' {
+ cfg.render_static()
outputs := cfg.render()
if outputs.len == 0 {
println('No documentation for $dirs')
@@ -776,12 +829,8 @@ fn (mut cfg DocConfig) generate_docs_from_file() {
}
}
}
- outputs := cfg.render()
- for file_name, content in outputs {
- output_path := os.join_path(cfg.output_path, file_name)
- println('Generating ${output_path}...')
- os.write_file(output_path, content)
- }
+ cfg.render_static()
+ cfg.render_parallel()
}
}
@@ -929,8 +978,8 @@ fn main() {
}
'-f' {
format := cmdline.option(current_args, '-f', '')
- allowed_str := allowed_formats.join(', ')
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)
}