v / cmd / tools / vdoc / main.v
245 lines · 233 sloc · 6.47 KB · 15f0f8a481a3bb5162b38d269b0885846fcedd71
Raw
1module main
2
3import os
4import os.cmdline
5import term
6import document as doc
7import v.vmod
8
9const vexe = os.getenv_opt('VEXE') or { @VEXE }
10
11const vroot = os.dir(vexe)
12
13const allowed_formats = ['md', 'markdown', 'json', 'text', 'ansi', 'html', 'htm', 'none']
14
15enum RunExampleMode {
16 skip
17 run
18 check
19}
20
21struct Config {
22mut:
23 pub_only bool = true
24 show_loc bool // for plaintext
25 show_time bool // show the total time spend generating content
26 is_color bool
27 is_multi bool
28 is_vlib bool
29 is_verbose bool
30 include_readme bool
31 include_examples bool = true
32 include_comments bool // for plaintext
33 inline_assets bool
34 theme_dir string = default_theme
35 no_timestamp bool
36 output_path string
37 output_type OutputType = .unset
38 input_path string
39 symbol_name string
40 platform doc.Platform
41 run_examples RunExampleMode // `-unsafe-run-examples` will run all `// Example: assert mod.abc() == y` comments in the processed modules.
42 // -check-examples will only check/validate that they compile, but without running any code.
43 // The options below are useful for generating a more stable HTML, that is easier to regression test:
44 html_only_contents bool // `-html-only-contents` will produce only the content of any given page, without styling tags etc.
45 html_no_vhash bool // `-html-no-vhash` will remove the version hash from the generated html
46 html_no_assets bool // `-html-no-assets` will not include CSS and JS asset tags in the generated html
47 html_no_right bool // `-html-no-right` will not add the doc-toc right panel in the generated html
48 html_no_toc_urls bool // `-html-no-toc-urls` will not add the toc_links panel in the generated html
49 html_no_footer bool // `-html-no-footer` will not add the footer panel in the generated html
50}
51
52fn main() {
53 unbuffer_stdout() // avoid the need for flush_stdout() calls
54 if os.args.len < 2 || '-h' in os.args || '-help' in os.args || '--help' in os.args
55 || os.args[1..] == ['doc', 'help'] {
56 os.system('${os.quoted_path(vexe)} help doc')
57 exit(0)
58 }
59 args := os.args[2..].clone()
60 cfg := parse_arguments(args)
61 if cfg.input_path == '' {
62 eprintln('vdoc: No input path found.')
63 exit(1)
64 }
65 // Config is immutable from this point on
66 mut vd := &VDoc{
67 cfg: cfg
68 manifest: vmod.Manifest{}
69 }
70 vd.vprintln('Setting output type to "${cfg.output_type}"')
71 vd.generate_docs_from_file()
72 if cfg.run_examples != .skip {
73 if vd.example_oks == 0 && vd.example_failures == 0 {
74 println(term.colorize(term.bright_yellow, 'Found NO examples.'))
75 } else {
76 println(term.colorize(term.gray, 'Found ${vd.example_oks} ok examples.'))
77 }
78 if vd.example_failures > 0 {
79 println(term.colorize(term.red, 'Found ${vd.example_failures} failing examples.'))
80 exit(1)
81 }
82 }
83}
84
85fn parse_arguments(args []string) Config {
86 mut cfg := Config{}
87 cfg.is_color = term.can_show_color_on_stdout()
88 mut is_color_was_set_explicitly := false
89 for i := 0; i < args.len; i++ {
90 arg := args[i]
91 current_args := args[i..]
92 match arg {
93 '-all' {
94 cfg.pub_only = false
95 }
96 '-f' {
97 format := cmdline.option(current_args, '-f', '')
98 if format !in allowed_formats {
99 allowed_str := allowed_formats.join(', ')
100 eprintln('vdoc: "${format}" is not a valid format. Only ${allowed_str} are allowed.')
101 exit(1)
102 }
103 cfg.output_type = set_output_type_from_str(format)
104 i++
105 }
106 '-color' {
107 cfg.is_color = true
108 is_color_was_set_explicitly = true
109 }
110 '-no-color' {
111 cfg.is_color = false
112 is_color_was_set_explicitly = true
113 }
114 '-inline-assets' {
115 cfg.inline_assets = true
116 }
117 '-theme-dir' {
118 cfg.theme_dir = cmdline.option(current_args, '-theme-dir', default_theme)
119 }
120 '-l' {
121 cfg.show_loc = true
122 }
123 '-comments' {
124 cfg.include_comments = true
125 }
126 '-m' {
127 cfg.is_multi = true
128 }
129 '-o' {
130 opath := cmdline.option(current_args, '-o', '')
131 cfg.output_path = if opath in ['stdout', '-'] { opath } else { os.real_path(opath) }
132 i++
133 }
134 '-os' {
135 platform_str := cmdline.option(current_args, '-os', '')
136 if platform_str == 'cross' {
137 eprintln('`v doc -os cross` is not supported yet.')
138 exit(1)
139 }
140 selected_platform := doc.platform_from_string(platform_str) or {
141 eprintln(err.msg())
142 exit(1)
143 }
144 cfg.platform = selected_platform
145 i++
146 }
147 '-time' {
148 cfg.show_time = true
149 }
150 '-check-examples' {
151 cfg.run_examples = .check
152 }
153 '-unsafe-run-examples' {
154 cfg.run_examples = .run
155 }
156 '-run-examples' {
157 eprintln('WARNING: the `-run-examples` option is deprecated, and will be removed after 2025-11-13.')
158 eprintln(' Use `-unsafe-run-examples` instead of `-run-examples` .')
159 cfg.run_examples = .run
160 }
161 '-no-timestamp' {
162 cfg.no_timestamp = true
163 }
164 '-no-examples' {
165 cfg.include_examples = false
166 }
167 //
168 '-html-only-contents' {
169 cfg.html_only_contents = true
170 }
171 '-html-no-vhash' {
172 cfg.html_no_vhash = true
173 }
174 '-html-no-assets' {
175 cfg.html_no_assets = true
176 }
177 '-html-no-right' {
178 cfg.html_no_right = true
179 }
180 '-html-no-toc-urls' {
181 cfg.html_no_toc_urls = true
182 }
183 '-html-no-footer' {
184 cfg.html_no_footer = true
185 }
186 //
187 '-readme' {
188 cfg.include_readme = true
189 }
190 '-v' {
191 cfg.is_verbose = true
192 }
193 else {
194 if cfg.input_path == '' {
195 cfg.input_path = arg
196 } else if !cfg.is_multi {
197 // Symbol name filtering should not be enabled
198 // in multi-module documentation mode.
199 cfg.symbol_name = arg
200 }
201 if i == args.len - 1 {
202 break
203 }
204 }
205 }
206 }
207
208 if cfg.output_type == .html {
209 // quirks specific to *just* the html output mode:
210 if cfg.output_path in ['stdout', '-'] {
211 cfg.inline_assets = true
212 }
213 }
214
215 if !is_color_was_set_explicitly {
216 if cfg.output_type == .plaintext {
217 cfg.is_color = false
218 } else if cfg.output_type == .ansi {
219 cfg.is_color = true
220 }
221 }
222
223 if cfg.is_color {
224 os.setenv('VCOLORS', 'always', true)
225 } else {
226 os.setenv('VCOLORS', 'never', true)
227 }
228
229 cfg.input_path = cfg.input_path.replace('\\', '/')
230 is_path := cfg.input_path.ends_with('.v') || cfg.input_path.split('/').len > 1
231 || cfg.input_path == '.'
232 if cfg.input_path.trim_right('/') == 'vlib' {
233 cfg.is_vlib = true
234 cfg.is_multi = true
235 cfg.input_path = os.join_path(vroot, 'vlib')
236 } else if !is_path {
237 mod_path := doc.lookup_module(cfg.input_path) or {
238 eprintln('vdoc: ${err}')
239 exit(1)
240 }
241 cfg.input_path = mod_path
242 }
243 cfg.input_path = cfg.input_path.replace('/', os.path_separator)
244 return cfg
245}
246