-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparse_dispatch.go
More file actions
142 lines (128 loc) · 3.9 KB
/
parse_dispatch.go
File metadata and controls
142 lines (128 loc) · 3.9 KB
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
142
package repomap
import (
"context"
"os"
"path/filepath"
"time"
"golang.org/x/sync/errgroup"
)
// absPath returns the absolute path for a file relative to the project root.
func (m *Map) absPath(rel string) string {
return filepath.Join(m.root, rel)
}
// parseFiles parses all discovered files in parallel and returns the symbols
// and a path→mtime map for stale checking.
// Non-Go files use tree-sitter when available, then ctags, then regex.
func (m *Map) parseFiles(ctx context.Context, files []FileInfo) ([]*FileSymbols, map[string]time.Time, error) {
mtimes := make(map[string]time.Time, len(files))
for _, fi := range files {
absPath := m.absPath(fi.Path)
info, err := os.Stat(absPath)
if err != nil {
continue
}
mtimes[absPath] = info.ModTime()
}
var (
goParsed []*FileSymbols
nonGoParsed []*FileSymbols
)
eg, egCtx := errgroup.WithContext(ctx)
eg.Go(func() error {
goFiles := make([]FileInfo, 0, len(files))
for _, fi := range files {
if fi.Language == "go" {
goFiles = append(goFiles, fi)
}
}
goParsed = parallelParse(goFiles, func(fi FileInfo) *FileSymbols {
sym, err := ParseGoFile(m.absPath(fi.Path), m.root)
if err != nil {
return nil
}
return sym
})
return nil
})
eg.Go(func() error {
nonGoParsed = m.parseNonGoFiles(egCtx, files)
return nil
})
if err := eg.Wait(); err != nil {
return nil, nil, err
}
parsed := make([]*FileSymbols, 0, len(goParsed)+len(nonGoParsed))
parsed = append(parsed, goParsed...)
parsed = append(parsed, nonGoParsed...)
// Apply blocklist filter once for all parse methods (ast/treesitter/ctags/regex).
for _, fs := range parsed {
m.blocklist.filterSymbols(fs)
}
DetectImplementations(parsed)
return parsed, mtimes, nil
}
// parseNonGoFiles parses non-Go files using the tiered fallback:
// tree-sitter → ctags → regex. Filters out Go files once at the entry so
// downstream stages can assume a non-Go file slice.
func (m *Map) parseNonGoFiles(ctx context.Context, files []FileInfo) []*FileSymbols {
nonGo := make([]FileInfo, 0, len(files))
for _, fi := range files {
if fi.Language != "go" {
nonGo = append(nonGo, fi)
}
}
if len(nonGo) == 0 {
return nil
}
if m.tsAvailable {
tsParsed, fallbackFiles := m.parseTreeSitterFiles(ctx, nonGo)
if len(fallbackFiles) > 0 {
fallbackParsed := m.parseWithCtagsOrRegex(ctx, fallbackFiles)
tsParsed = append(tsParsed, fallbackParsed...)
}
return tsParsed
}
return m.parseWithCtagsOrRegex(ctx, nonGo)
}
// parseWithCtagsOrRegex tries ctags, then falls back to regex parsing.
func (m *Map) parseWithCtagsOrRegex(ctx context.Context, files []FileInfo) []*FileSymbols {
if m.ctagsAvailable {
ctagsParsed, err := ParseWithCtags(ctx, m.root, files)
if err == nil {
return ctagsParsed
}
}
return m.parseGenericFiles(files)
}
// parseGenericFiles parses non-Go files using regex patterns in parallel.
// Caller must pass only non-Go files (enforced by parseNonGoFiles).
func (m *Map) parseGenericFiles(files []FileInfo) []*FileSymbols {
return parallelParse(files, func(fi FileInfo) *FileSymbols {
sym, err := ParseGenericFile(m.absPath(fi.Path), m.root, fi.Language)
if err != nil {
return nil
}
return sym
})
}
// parseNonGoFile parses a single non-Go file using the standalone ladder:
// tree-sitter (when available) → regex. No ctags step — ctags only pays off
// as a batch operation, so serial callers (commit analyze) skip it. Used
// where no Map instance exists. Returns nil on total miss.
func parseNonGoFile(abs, root, lang string) *FileSymbols {
if TreeSitterAvailable() {
if data, err := os.ReadFile(abs); err == nil {
if sym := parseWithTreeSitter(data, lang, relPath(root, abs)); sym != nil {
if sym.ImportPath == "" {
sym.ImportPath = deriveImportPath(abs, root, lang, splitLines(string(data)))
}
return sym
}
}
}
sym, err := ParseGenericFile(abs, root, lang)
if err != nil {
return nil
}
return sym
}