@@ -35,6 +35,25 @@ pub mut:
3535 errors int
3636}
3737
38+ struct VCheckIgnoreRule {
39+ base_dir string
40+ pattern string
41+ }
42+
43+ struct VCheckIgnoreContext {
44+ repo_root string
45+ }
46+
47+ struct VCheckIgnoreMatch {
48+ ignore_file string
49+ pattern string
50+ }
51+
52+ struct MDPathScanResult {
53+ files []string
54+ skipped int
55+ }
56+
3857fn (v1 CheckResult) + (v2 CheckResult) CheckResult {
3958 return CheckResult{
4059 files: v1 .files + v2 .files
@@ -67,10 +86,13 @@ fn main() {
6786 os.rmdir_all (vcheckfolder) or {}
6887 }
6988 mut all_mdfiles := []MDFile{}
89+ mut skipped_mdfiles := 0
7090 for i := 0 ; i < files_paths.len; i++ {
7191 file_path := files_paths[i]
7292 if os.is_dir (file_path) {
73- files_paths << md_file_paths (file_path)
93+ scan_result := md_file_paths (file_path)
94+ files_paths << scan_result.files
95+ skipped_mdfiles + = scan_result.skipped
7496 continue
7597 }
7698 real_path := os.real_path (file_path)
@@ -85,7 +107,12 @@ fn main() {
85107 lines: lines
86108 }
87109 }
88- println ('> Found: ${all_mdfiles.len} .md files.' )
110+ println ('> Found: ${all_mdfiles.len} .md files. Skipped by .vcheckignore: ${skipped_mdfiles} .' )
111+ if is_verbose {
112+ for idx, mdfile in all_mdfiles {
113+ println ('> file ${idx + 1} is ${mdfile.path} ' )
114+ }
115+ }
89116 if show_progress {
90117 // this is intended to be replaced by the progress lines
91118 println ('' )
@@ -110,17 +137,160 @@ fn main() {
110137 }
111138}
112139
113- fn md_file_paths (dir string ) [] string {
140+ fn md_file_paths (dir string ) MDPathScanResult {
114141 mut files_to_check := []string {}
142+ mut skipped := 0
143+ vcheckignore := collect_vcheckignore_context (dir)
115144 md_files := os.walk_ext (dir, '.md' )
116145 for file in md_files {
117146 nfile := file.replace ('\\ ' , '/' )
118147 if nfile.contains_any_substr (['/thirdparty/' , 'CHANGELOG' , '/testdata/' ]) {
119148 continue
120149 }
150+ if skip_match := vcheckignore.skip_match (file) {
151+ if is_verbose {
152+ println ('SKIP: ${vcheckignore.repo_relative_path(file)} (from ${vcheckignore.repo_relative_path(skip_match.ignore_file)} : ${skip_match.pattern} )' )
153+ }
154+ skipped++
155+ continue
156+ }
121157 files_to_check << file
122158 }
123- return files_to_check
159+ return MDPathScanResult{
160+ files: files_to_check
161+ skipped: skipped
162+ }
163+ }
164+
165+ fn collect_vcheckignore_context (cwd string ) VCheckIgnoreContext {
166+ repo_root := find_repo_root (cwd)
167+ return VCheckIgnoreContext{
168+ repo_root: repo_root
169+ }
170+ }
171+
172+ fn find_repo_root (cwd string ) string {
173+ mut dir := os.real_path (cwd)
174+ for {
175+ if os.exists (os.join_path (dir, '.git' )) {
176+ return dir
177+ }
178+ parent := os.dir (dir)
179+ if parent == dir || parent == '' {
180+ return dir
181+ }
182+ dir = parent
183+ }
184+ return dir
185+ }
186+
187+ fn (ctx VCheckIgnoreContext) skip_match (file_path string ) ? VCheckIgnoreMatch {
188+ file := os.real_path (file_path).replace ('\\ ' , '/' )
189+ mut dir := os.dir (file)
190+ repo_root := ctx.repo_root.replace ('\\ ' , '/' )
191+ for {
192+ ignore_path := os.join_path (dir, '.vcheckignore' )
193+ if os.is_file (ignore_path) {
194+ lines := os.read_lines (ignore_path) or { []string {} }
195+ for line in lines {
196+ pattern := normalize_vcheckignore_line (line)
197+ if pattern == '' || pattern.starts_with ('#' ) {
198+ continue
199+ }
200+ if matches_vcheckignore_rule (file, VCheckIgnoreRule{
201+ base_dir: dir
202+ pattern: pattern
203+ })
204+ {
205+ return VCheckIgnoreMatch{
206+ ignore_file: ignore_path
207+ pattern: pattern
208+ }
209+ }
210+ }
211+ }
212+ if dir.replace ('\\ ' , '/' ) == repo_root {
213+ break
214+ }
215+ parent := os.dir (dir)
216+ if parent == dir || parent == '' {
217+ break
218+ }
219+ dir = parent
220+ }
221+ return none
222+ }
223+
224+ fn normalize_vcheckignore_line (line string ) string {
225+ trimmed := line.trim_space ()
226+ if trimmed == '' {
227+ return ''
228+ }
229+ if comment_idx := trimmed.index ('#' ) {
230+ return trimmed[..comment_idx].trim_space ()
231+ }
232+ return trimmed
233+ }
234+
235+ fn (ctx VCheckIgnoreContext) repo_relative_path (file_path string ) string {
236+ file := os.real_path (file_path).replace ('\\ ' , '/' )
237+ root := ctx.repo_root.replace ('\\ ' , '/' )
238+ root_prefix := root + '/'
239+ if file.starts_with (root_prefix) {
240+ return file.all_after (root_prefix)
241+ }
242+ return file
243+ }
244+
245+ fn matches_vcheckignore_rule (file string , rule VCheckIgnoreRule) bool {
246+ base := rule.base_dir.replace ('\\ ' , '/' )
247+ base_prefix := base + '/'
248+ if ! file.starts_with (base_prefix) {
249+ return false
250+ }
251+ relative_file := file.all_after (base_prefix)
252+ mut pattern := rule.pattern.replace ('\\ ' , '/' )
253+ if pattern.starts_with ('!' ) {
254+ return false
255+ }
256+ mut anchored := false
257+ if pattern.starts_with ('/' ) {
258+ anchored = true
259+ pattern = pattern.trim_left ('/' )
260+ }
261+ if pattern.ends_with ('/' ) {
262+ pattern = pattern.trim_right ('/' )
263+ return matches_vcheckignore_directory_pattern (relative_file, pattern, anchored)
264+ }
265+ if anchored {
266+ return relative_file.match_glob (pattern)
267+ }
268+ if pattern.contains ('/' ) {
269+ return relative_file.match_glob (pattern)
270+ }
271+ return os.file_name (relative_file).match_glob (pattern)
272+ }
273+
274+ fn matches_vcheckignore_directory_pattern (relative_file string , pattern string , anchored bool ) bool {
275+ mut relative_dir := os.dir (relative_file).replace ('\\ ' , '/' )
276+ if relative_dir == '.' || relative_dir == '' {
277+ return false
278+ }
279+ if anchored {
280+ return relative_dir.match_glob (pattern) || relative_dir.match_glob (pattern + '/*' )
281+ }
282+ mut candidate := relative_dir
283+ for {
284+ if candidate.match_glob (pattern) || candidate.match_glob (pattern + '/*' ) {
285+ return true
286+ }
287+ if slash_idx := candidate.index ('/' ) {
288+ candidate = candidate[slash_idx + 1 ..]
289+ continue
290+ }
291+ break
292+ }
293+ return false
124294}
125295
126296fn wprintln (s string ) {
0 commit comments