1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
// Copyright (c) 2020 Lars Pontoppidan. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
import os
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.'
)
struct UndocumentedFN {
line int
signature string
tags []string
}
struct Options {
show_help bool
collect_tags bool
deprecated bool
}
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
}
fn report_undocumented_functions_in_path(opt Options, path string) {
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)
for file in files {
if file.ends_with('_test.v') {
continue
}
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
}
}
if grab {
clean_line := line.all_before_last(' {')
info << UndocumentedFN{i + 1, clean_line, tags}
}
}
}
}
}
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')
}
}
}
}
}
fn collect_tags(line string) []string {
mut cleaned := line.all_before('/')
cleaned = cleaned.replace_each(['[', '', ']', '', ' ', ''])
return cleaned.split(',')
}
fn main() {
if os.args.len == 1 {
println('Usage: $tool_name PATH \n$tool_description\n$tool_name -h for more help...')
exit(1)
}
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)
}
for path in os.args[1..] {
if os.is_file(path) {
report_undocumented_functions_in_file(opt, path)
} else {
report_undocumented_functions_in_path(opt, path)
}
}
}
|