@mrclrchtr/supi-code-intelligence 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/pattern-matcher.ts +11 -184
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +2 -2
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-lsp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "SuPi LSP extension — Language Server Protocol integration for pi",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
"!__tests__"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
+
"ignore": "^7.0.5",
|
|
24
25
|
"typescript": "6.0.3",
|
|
25
|
-
"@mrclrchtr/supi-core": "1.
|
|
26
|
+
"@mrclrchtr/supi-core": "1.6.0"
|
|
26
27
|
},
|
|
27
28
|
"bundledDependencies": [
|
|
28
29
|
"@mrclrchtr/supi-core"
|
|
@@ -1,20 +1,15 @@
|
|
|
1
|
+
import ignore from "ignore";
|
|
2
|
+
|
|
1
3
|
/** Gitignore-style glob pattern matching for path exclusion.
|
|
2
4
|
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Normalize path separators to forward slashes and trim.
|
|
5
|
+
* Delegates to the {@link https://github.com/kaelzhang/node-ignore | ignore} package,
|
|
6
|
+
* which provides battle-tested .gitignore semantics used by ESLint, Prettier, and others.
|
|
7
|
+
*
|
|
8
|
+
* Supports full gitignore syntax: literal names, `*` / `?` wildcards, `**` recursive globs,
|
|
9
|
+
* leading `/` anchored patterns, trailing `/` directory-only patterns, and `!` negation.
|
|
10
|
+
*
|
|
11
|
+
* **Note:** Patterns that start with `#` are treated as comments unless escaped with `\#`.
|
|
14
12
|
*/
|
|
15
|
-
function normalize(p: string): string {
|
|
16
|
-
return p.replaceAll("\\", "/").trim();
|
|
17
|
-
}
|
|
18
13
|
|
|
19
14
|
/**
|
|
20
15
|
* Check whether a project-relative file path matches a gitignore-style glob pattern.
|
|
@@ -24,174 +19,6 @@ function normalize(p: string): string {
|
|
|
24
19
|
* @returns `true` if the file path matches the pattern
|
|
25
20
|
*/
|
|
26
21
|
export function isGlobMatch(filePath: string, pattern: string): boolean {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (!fp || !pat) return false;
|
|
30
|
-
|
|
31
|
-
// Leading / → anchored to root
|
|
32
|
-
const anchored = pat.startsWith("/");
|
|
33
|
-
const noLeadingSlash = anchored ? pat.slice(1) : pat;
|
|
34
|
-
|
|
35
|
-
// Trailing / → directory-only
|
|
36
|
-
const dirOnly = noLeadingSlash.endsWith("/");
|
|
37
|
-
const cleanPat = dirOnly ? noLeadingSlash.slice(0, -1) : noLeadingSlash;
|
|
38
|
-
|
|
39
|
-
if (!cleanPat) return false;
|
|
40
|
-
|
|
41
|
-
return matchGlob(fp, cleanPat, { anchored, dirOnly });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
interface MatchOptions {
|
|
45
|
-
anchored: boolean;
|
|
46
|
-
dirOnly: boolean;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Core recursive pattern matching against a multi-segment path.
|
|
51
|
-
*/
|
|
52
|
-
function matchGlob(filePath: string, pattern: string, opts: MatchOptions): boolean {
|
|
53
|
-
// Direct match
|
|
54
|
-
if (!opts.anchored && !opts.dirOnly && filePath === pattern) return true;
|
|
55
|
-
|
|
56
|
-
// Split into segments
|
|
57
|
-
const pathSegments = filePath.split("/");
|
|
58
|
-
const patternSegments = pattern.split("/");
|
|
59
|
-
|
|
60
|
-
// ** recursive glob
|
|
61
|
-
if (pattern.startsWith("**/")) {
|
|
62
|
-
const suffix = pattern.slice(3);
|
|
63
|
-
return (
|
|
64
|
-
matchGlob(filePath, suffix, { ...opts, anchored: false }) ||
|
|
65
|
-
starStarMatch(pathSegments, suffix)
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// prefix/**/suffix bounded recursive glob
|
|
70
|
-
const dstarIdx = pattern.indexOf("/**/");
|
|
71
|
-
if (dstarIdx !== -1) {
|
|
72
|
-
const prefix = pattern.slice(0, dstarIdx);
|
|
73
|
-
const suffix = pattern.slice(dstarIdx + 4);
|
|
74
|
-
return matchBoundedStar(pathSegments, prefix, suffix);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Single-segment patterns
|
|
78
|
-
if (patternSegments.length === 1 && !opts.anchored) {
|
|
79
|
-
return matchSingleSegment(pathSegments, patternSegments[0], opts.dirOnly);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Multi-segment: anchored or unanchored
|
|
83
|
-
if (opts.anchored) {
|
|
84
|
-
return matchSegments(pathSegments, patternSegments, opts.dirOnly);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Unanchored multi-segment: try at each starting position
|
|
88
|
-
return matchUnanchoredSegments(pathSegments, patternSegments, opts.dirOnly);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Match a ** recursive suffix across directory levels.
|
|
93
|
-
*/
|
|
94
|
-
function starStarMatch(segments: string[], suffix: string): boolean {
|
|
95
|
-
for (let i = 0; i < segments.length; i++) {
|
|
96
|
-
const remaining = segments.slice(i).join("/");
|
|
97
|
-
if (matchGlob(remaining, suffix, { anchored: false, dirOnly: false })) return true;
|
|
98
|
-
}
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Match prefix plus star-star-slash plus suffix bounded recursive pattern. */
|
|
103
|
-
function matchBoundedStar(segments: string[], prefix: string, suffix: string): boolean {
|
|
104
|
-
// Try to find a split point where left matches prefix and right matches suffix
|
|
105
|
-
for (let i = 1; i < segments.length; i++) {
|
|
106
|
-
const left = segments.slice(0, i).join("/");
|
|
107
|
-
const right = segments.slice(i).join("/");
|
|
108
|
-
if (
|
|
109
|
-
matchGlob(left, prefix, { anchored: false, dirOnly: false }) &&
|
|
110
|
-
matchGlob(right, suffix, { anchored: false, dirOnly: false })
|
|
111
|
-
) {
|
|
112
|
-
return true;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Match a single-segment pattern against path segments.
|
|
120
|
-
*/
|
|
121
|
-
function matchSingleSegment(segments: string[], patternSeg: string, dirOnly: boolean): boolean {
|
|
122
|
-
const hasGlob = patternSeg.includes("*") || patternSeg.includes("?");
|
|
123
|
-
|
|
124
|
-
if (hasGlob) {
|
|
125
|
-
// Glob pattern matches any segment
|
|
126
|
-
return segments.some((seg) => simpleMatch(seg, patternSeg));
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Literal segment name
|
|
130
|
-
if (dirOnly) {
|
|
131
|
-
// Match as directory: any segment except the last (file) one
|
|
132
|
-
return segments.slice(0, -1).some((seg) => seg === patternSeg);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Match any segment (file or directory)
|
|
136
|
-
return segments.some((seg) => seg === patternSeg);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Try to match multi-segment pattern at each start position.
|
|
141
|
-
*/
|
|
142
|
-
function matchUnanchoredSegments(segments: string[], pattern: string[], dirOnly: boolean): boolean {
|
|
143
|
-
for (let i = 0; i < segments.length; i++) {
|
|
144
|
-
if (matchSegments(segments.slice(i), pattern, dirOnly)) return true;
|
|
145
|
-
}
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/** Match segments from start. Returns true if all pattern segments match contiguously. */
|
|
150
|
-
function matchSegments(segments: string[], pattern: string[], dirOnly: boolean): boolean {
|
|
151
|
-
if (pattern.length > segments.length) return false;
|
|
152
|
-
|
|
153
|
-
for (let i = 0; i < pattern.length; i++) {
|
|
154
|
-
if (!matchSegmentAtIndex(segments, pattern, i)) return false;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// dirOnly: last matched segment must not be the last path segment
|
|
158
|
-
if (dirOnly && pattern.length === segments.length) return false;
|
|
159
|
-
|
|
160
|
-
return true;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/** Match a single pattern segment against the corresponding path segment. */
|
|
164
|
-
function matchSegmentAtIndex(segments: string[], pattern: string[], index: number): boolean {
|
|
165
|
-
const patSeg = pattern[index];
|
|
166
|
-
const pathSeg = segments[index];
|
|
167
|
-
|
|
168
|
-
if (patSeg === "**") {
|
|
169
|
-
// ** at end matches all remaining segments
|
|
170
|
-
if (index === pattern.length - 1) return true;
|
|
171
|
-
// Try rest of pattern from various positions
|
|
172
|
-
for (let j = index; j < segments.length; j++) {
|
|
173
|
-
if (matchSegments(segments.slice(j), pattern.slice(index + 1), false)) return true;
|
|
174
|
-
}
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return simpleMatch(pathSeg, patSeg);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Match a single path segment against a single pattern segment.
|
|
183
|
-
* Supports `*` (any chars except `/`) and `?` (single char).
|
|
184
|
-
*/
|
|
185
|
-
function simpleMatch(segment: string, pattern: string): boolean {
|
|
186
|
-
if (pattern === "*") return true;
|
|
187
|
-
if (pattern === segment) return true;
|
|
188
|
-
if (!pattern.includes("*") && !pattern.includes("?")) return false;
|
|
189
|
-
|
|
190
|
-
// Convert pattern to simple regex
|
|
191
|
-
const regexStr = pattern
|
|
192
|
-
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
193
|
-
.replace(/\*/g, "[^/]*")
|
|
194
|
-
.replace(/\?/g, "[^/]");
|
|
195
|
-
|
|
196
|
-
return new RegExp(`^${regexStr}$`).test(segment);
|
|
22
|
+
if (!filePath || !pattern) return false;
|
|
23
|
+
return ignore().add(pattern).ignores(filePath);
|
|
197
24
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-tree-sitter",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "SuPi Tree-sitter extension — structural AST analysis for pi",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"web-tree-sitter": "^0.26.8",
|
|
27
|
-
"@mrclrchtr/supi-core": "1.
|
|
27
|
+
"@mrclrchtr/supi-core": "1.6.0"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"@earendil-works/pi-ai": "*",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-code-intelligence",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "SuPi Code Intelligence extension — architecture briefs, caller/callee analysis, impact assessment, and pattern search for pi",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"src/**/*.ts"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@mrclrchtr/supi-core": "1.
|
|
23
|
-
"@mrclrchtr/supi-
|
|
24
|
-
"@mrclrchtr/supi-
|
|
22
|
+
"@mrclrchtr/supi-core": "1.6.0",
|
|
23
|
+
"@mrclrchtr/supi-lsp": "1.6.0",
|
|
24
|
+
"@mrclrchtr/supi-tree-sitter": "1.6.0"
|
|
25
25
|
},
|
|
26
26
|
"bundledDependencies": [
|
|
27
27
|
"@mrclrchtr/supi-core",
|