2020-12-05 13:10:54 +03:00
|
|
|
// Copyright (c) 2020 Lars Pontoppidan. All rights reserved.
|
2020-12-11 19:35:25 +03:00
|
|
|
// Use of this source code is governed by an MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
2020-12-05 13:10:54 +03:00
|
|
|
import os
|
2020-12-11 19:35:25 +03:00
|
|
|
import flag
|
|
|
|
|
|
|
|
const (
|
|
|
|
tool_name = os.file_name(os.executable())
|
|
|
|
tool_version = '0.0.2'
|
|
|
|
tool_description = 'Prints all V functions in .v files under PATH/, that do not yet have documentation comments.'
|
|
|
|
)
|
2020-12-05 13:10:54 +03:00
|
|
|
|
|
|
|
struct UndocumentedFN {
|
|
|
|
line int
|
|
|
|
signature string
|
2020-12-11 19:35:25 +03:00
|
|
|
tags []string
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Options {
|
|
|
|
show_help bool
|
|
|
|
collect_tags bool
|
|
|
|
deprecated bool
|
2020-12-05 13:10:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn collect(path string, mut l []string, f fn (string, mut []string)) {
|
|
|
|
if !os.is_dir(path) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
mut files := os.ls(path) or { return }
|
|
|
|
for file in files {
|
|
|
|
p := path + os.path_separator + file
|
|
|
|
if os.is_dir(p) && !os.is_link(p) {
|
|
|
|
collect(p, mut l, f)
|
|
|
|
} else if os.exists(p) {
|
|
|
|
f(p, mut l)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-12-11 19:35:25 +03:00
|
|
|
fn report_undocumented_functions_in_path(opt Options, path string) {
|
2020-12-05 13:10:54 +03:00
|
|
|
mut files := []string{}
|
|
|
|
collect_fn := fn (path string, mut l []string) {
|
|
|
|
if os.file_ext(path) == '.v' {
|
|
|
|
l << os.real_path(path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
collect(path, mut files, collect_fn)
|
2021-01-05 17:13:01 +03:00
|
|
|
for file in files {
|
|
|
|
if file.ends_with('_test.v') {
|
2020-12-05 13:10:54 +03:00
|
|
|
continue
|
|
|
|
}
|
2021-01-05 17:13:01 +03:00
|
|
|
report_undocumented_functions_in_file(opt, file)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn report_undocumented_functions_in_file(opt Options, file string) {
|
|
|
|
contents := os.read_file(file) or { panic(err) }
|
|
|
|
lines := contents.split('\n')
|
|
|
|
mut info := []UndocumentedFN{}
|
|
|
|
for i, line in lines {
|
|
|
|
if line.starts_with('pub fn') ||
|
|
|
|
(line.starts_with('fn ') && !(line.starts_with('fn C.') || line.starts_with('fn main'))) {
|
|
|
|
// println('Match: $line')
|
|
|
|
if i > 0 && lines.len > 0 {
|
|
|
|
mut line_above := lines[i - 1]
|
|
|
|
if !line_above.starts_with('//') {
|
|
|
|
mut tags := []string{}
|
|
|
|
mut grab := true
|
|
|
|
for j := i - 1; j >= 0; j-- {
|
|
|
|
prev_line := lines[j]
|
|
|
|
if prev_line.contains('}') { // We've looked back to the above scope, stop here
|
|
|
|
break
|
|
|
|
} else if prev_line.starts_with('[') {
|
|
|
|
tags << collect_tags(prev_line)
|
|
|
|
continue
|
|
|
|
} else if prev_line.starts_with('//') { // Single-line comment
|
|
|
|
grab = false
|
|
|
|
break
|
2020-12-05 13:10:54 +03:00
|
|
|
}
|
|
|
|
}
|
2021-01-05 17:13:01 +03:00
|
|
|
if grab {
|
|
|
|
clean_line := line.all_before_last(' {')
|
|
|
|
info << UndocumentedFN{i + 1, clean_line, tags}
|
|
|
|
}
|
2020-12-05 13:10:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-05 17:13:01 +03:00
|
|
|
}
|
|
|
|
if info.len > 0 {
|
|
|
|
for undocumented_fn in info {
|
|
|
|
tags_str := if opt.collect_tags && undocumented_fn.tags.len > 0 { '$undocumented_fn.tags' } else { '' }
|
|
|
|
if opt.deprecated {
|
|
|
|
println('$file:$undocumented_fn.line:0:$undocumented_fn.signature $tags_str')
|
|
|
|
} else {
|
|
|
|
if 'deprecated' !in undocumented_fn.tags {
|
|
|
|
println('$file:$undocumented_fn.line:0:$undocumented_fn.signature $tags_str')
|
2020-12-11 19:35:25 +03:00
|
|
|
}
|
2020-12-05 13:10:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-11 19:35:25 +03:00
|
|
|
fn collect_tags(line string) []string {
|
|
|
|
mut cleaned := line.all_before('/')
|
|
|
|
cleaned = cleaned.replace_each(['[', '', ']', '', ' ', ''])
|
|
|
|
return cleaned.split(',')
|
|
|
|
}
|
|
|
|
|
2020-12-05 13:10:54 +03:00
|
|
|
fn main() {
|
|
|
|
if os.args.len == 1 {
|
2020-12-11 19:35:25 +03:00
|
|
|
println('Usage: $tool_name PATH \n$tool_description\n$tool_name -h for more help...')
|
2020-12-05 13:10:54 +03:00
|
|
|
exit(1)
|
|
|
|
}
|
2020-12-11 19:35:25 +03:00
|
|
|
mut fp := flag.new_flag_parser(os.args[1..])
|
|
|
|
fp.application(tool_name)
|
|
|
|
fp.version(tool_version)
|
|
|
|
fp.description(tool_description)
|
|
|
|
fp.arguments_description('PATH [PATH]...')
|
|
|
|
// Collect tool options
|
|
|
|
opt := Options{
|
|
|
|
show_help: fp.bool('help', `h`, false, 'Show this help text.')
|
|
|
|
deprecated: fp.bool('deprecated', `d`, false, 'Include deprecated functions in output.')
|
|
|
|
collect_tags: fp.bool('tags', `t`, false, 'Also print function tags if any is found.')
|
|
|
|
}
|
|
|
|
if opt.show_help {
|
|
|
|
println(fp.usage())
|
|
|
|
exit(0)
|
|
|
|
}
|
2020-12-05 13:10:54 +03:00
|
|
|
for path in os.args[1..] {
|
2021-01-05 17:13:01 +03:00
|
|
|
if os.is_file(path) {
|
|
|
|
report_undocumented_functions_in_file(opt, path)
|
|
|
|
} else {
|
|
|
|
report_undocumented_functions_in_path(opt, path)
|
|
|
|
}
|
2020-12-05 13:10:54 +03:00
|
|
|
}
|
|
|
|
}
|