| 1 | module main |
| 2 | |
| 3 | import os |
| 4 | |
| 5 | struct IgnoreRules { |
| 6 | mut: |
| 7 | // Ignore patterns use the path with a `.vdocignore` file as a base. E.g.: |
| 8 | // `{'<path>': ['<pattern1>', '<pattern2>'], '<path/subpath>': ['<pattern3>']}` |
| 9 | patterns map[string][]string = { |
| 10 | // Default ignore patterns. |
| 11 | '': ['testdata', 'tests', '*_test.v'] |
| 12 | } |
| 13 | paths map[string]bool |
| 14 | } |
| 15 | |
| 16 | fn get_modules(path string) []string { |
| 17 | mut modules := map[string]bool{} |
| 18 | for p in get_paths(path, IgnoreRules.get(path)) { |
| 19 | modules[os.dir(p)] = true |
| 20 | } |
| 21 | mut res := modules.keys() |
| 22 | res.sort() |
| 23 | return res |
| 24 | } |
| 25 | |
| 26 | fn get_paths(path string, ignore_rules IgnoreRules) []string { |
| 27 | mut res := []string{} |
| 28 | outer: for p in os.ls(path) or { return [] } { |
| 29 | fp := os.join_path(path, p) |
| 30 | if fp in ignore_rules.paths { |
| 31 | continue |
| 32 | } |
| 33 | is_dir := os.is_dir(fp) |
| 34 | for ignore_path, patterns in ignore_rules.patterns { |
| 35 | if fp.starts_with(ignore_path) { |
| 36 | if patterns.any(p == it |
| 37 | || (it.contains('*') && p.ends_with(it.all_after('*'))) |
| 38 | || (is_dir && it.ends_with('/') && fp.ends_with(it.trim_right('/'))) |
| 39 | || (!it.ends_with('/') && it.contains('/') && fp.contains(it))) |
| 40 | { |
| 41 | continue outer |
| 42 | } |
| 43 | } |
| 44 | } |
| 45 | if is_dir { |
| 46 | res << get_paths(fp, ignore_rules) |
| 47 | continue |
| 48 | } |
| 49 | if p.ends_with('.v') { |
| 50 | res << fp |
| 51 | } |
| 52 | } |
| 53 | return res |
| 54 | } |
| 55 | |
| 56 | fn IgnoreRules.get(path string) IgnoreRules { |
| 57 | mut res := IgnoreRules{} |
| 58 | mut vdocignore_paths := []string{} |
| 59 | mut vdocignore_paths_ref := &vdocignore_paths |
| 60 | os.walk(path, fn [vdocignore_paths_ref] (p string) { |
| 61 | if os.file_name(p) == '.vdocignore' { |
| 62 | unsafe { |
| 63 | vdocignore_paths_ref << p |
| 64 | } |
| 65 | } |
| 66 | }) |
| 67 | for ignore_path in vdocignore_paths { |
| 68 | ignore_content := os.read_file(ignore_path) or { continue } |
| 69 | if ignore_content.trim_space() == '' { |
| 70 | continue |
| 71 | } |
| 72 | rules := ignore_content.split_into_lines().map(it.trim_space()) |
| 73 | for rule in rules { |
| 74 | if rule.starts_with('#') { |
| 75 | continue |
| 76 | } |
| 77 | if rule.contains('*.') || rule.contains('**') { |
| 78 | // Skip wildcards that are defined in an ignore file. |
| 79 | // For now, only add a basic implementation in `get_paths` |
| 80 | // that can handle the default `*_test.v` pattern. |
| 81 | eprintln('vdoc: Wildcards in ignore rules are not yet supported.') |
| 82 | continue |
| 83 | } |
| 84 | p := os.dir(ignore_path) |
| 85 | if rule.starts_with('/') { |
| 86 | // Similar to `.gitignore`, a pattern starting with `/` should only ignore |
| 87 | // the pattern relative to the directory of the `.vdocignore` file. |
| 88 | // `/a` should ignore `/a` but not `/b/a`. While `a` should ignore `/a` and `/b/a`. |
| 89 | res.paths[os.join_path(p, rule.trim_left('/'))] = true |
| 90 | } else { |
| 91 | if p !in res.patterns { |
| 92 | res.patterns[p] = []string{} |
| 93 | } |
| 94 | res.patterns[p] << rule |
| 95 | } |
| 96 | } |
| 97 | } |
| 98 | return res |
| 99 | } |
| 100 | |