@mrclrchtr/supi-code-intelligence 4.8.0 → 4.10.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.
Files changed (36) hide show
  1. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  2. package/node_modules/@mrclrchtr/supi-core/README.md +3 -1
  3. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  4. package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +8 -0
  5. package/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +120 -0
  6. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  7. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +3 -1
  8. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  9. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +8 -0
  10. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +120 -0
  11. package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
  12. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +155 -0
  13. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +70 -34
  14. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +104 -5
  15. package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +10 -0
  16. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  17. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +3 -1
  18. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  19. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +8 -0
  20. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +120 -0
  21. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
  22. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +4 -4
  23. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +1 -1
  24. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
  25. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +4 -4
  26. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +4 -3
  27. package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/kotlin/tree-sitter-kotlin.wasm.json +1 -1
  28. package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/sql/tree-sitter-sql.wasm.json +1 -1
  29. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime.ts +106 -2
  30. package/package.json +5 -5
  31. package/src/analysis/refactor/apply.ts +14 -19
  32. package/src/analysis/refactor/position.ts +47 -0
  33. package/src/analysis/refactor/safety.ts +18 -10
  34. package/src/analysis/search/ast-scan-timing.ts +57 -0
  35. package/src/analysis/search/pattern-analysis.ts +230 -0
  36. package/src/analysis/search/pattern.ts +22 -224
@@ -0,0 +1,230 @@
1
+ import type {
2
+ CodeResult,
3
+ OutlineData,
4
+ StructuralProvider as StructuralSubstrate,
5
+ } from "@mrclrchtr/supi-code-runtime/api";
6
+ import type { CodeFindAstKind } from "../../tool/find/ast-kinds.ts";
7
+ import type { AstScanLimitation } from "./ast-scan.ts";
8
+ import { callableExpressionForMatching } from "./call-name.ts";
9
+ import { settleByDeadline } from "./deadline.ts";
10
+ import { relativeDisplayPath } from "./paths.ts";
11
+
12
+ export interface StructuredPatternParams {
13
+ readonly pattern: string;
14
+ readonly kind: CodeFindAstKind;
15
+ }
16
+
17
+ export interface StructuredMatch {
18
+ readonly file: string;
19
+ readonly name: string;
20
+ readonly kind: string;
21
+ readonly line: number;
22
+ }
23
+
24
+ export type StructuredFailureKind = Exclude<CodeResult<never>, { kind: "success" }>["kind"];
25
+
26
+ export interface StructuredFailure {
27
+ readonly file: string;
28
+ readonly kind: StructuredFailureKind;
29
+ readonly reason: string;
30
+ }
31
+
32
+ export type StructuredScanLimitation = AstScanLimitation | ProviderScanLimitation;
33
+
34
+ export interface ProviderScanLimitation {
35
+ readonly reason: "provider-failure";
36
+ readonly pathCount: number;
37
+ readonly examples: readonly string[];
38
+ }
39
+
40
+ export interface StructuredFileAnalysis {
41
+ readonly matches: StructuredMatch[];
42
+ readonly failures: StructuredFailure[];
43
+ readonly limitations: StructuredScanLimitation[];
44
+ readonly analyzedFileCount: number;
45
+ }
46
+
47
+ interface AnalyzeStructuredFilesOptions {
48
+ readonly files: readonly string[];
49
+ readonly displayBase: string;
50
+ readonly params: StructuredPatternParams;
51
+ readonly structural: StructuralSubstrate;
52
+ readonly deadline: number;
53
+ readonly now: () => number;
54
+ readonly signal?: AbortSignal;
55
+ readonly initialLimitations: readonly AstScanLimitation[];
56
+ }
57
+
58
+ /** Analyze enumerated files through the operation-specific structural provider method. */
59
+ export async function analyzeStructuredFiles(
60
+ options: AnalyzeStructuredFilesOptions,
61
+ ): Promise<StructuredFileAnalysis> {
62
+ const matches: StructuredMatch[] = [];
63
+ const failures: StructuredFailure[] = [];
64
+ const limitations: StructuredScanLimitation[] = [...options.initialLimitations];
65
+ const matcher = createStructuredMatcher(options.params.pattern);
66
+ let analyzedFileCount = 0;
67
+
68
+ for (const [index, absoluteFile] of options.files.entries()) {
69
+ options.signal?.throwIfAborted();
70
+ if (options.now() > options.deadline) {
71
+ addAnalysisTimeout(limitations, options.files.slice(index), options.displayBase);
72
+ break;
73
+ }
74
+ const relativeFile = relativeDisplayPath(options.displayBase, absoluteFile);
75
+ const fileMatches: StructuredMatch[] = [];
76
+ const fileFailures: StructuredFailure[] = [];
77
+ const outcome = await settleByDeadline(
78
+ async () => {
79
+ try {
80
+ await collectMatchesForFile(
81
+ fileMatches,
82
+ fileFailures,
83
+ options.structural,
84
+ relativeFile,
85
+ options.params.kind,
86
+ matcher,
87
+ );
88
+ } catch (error) {
89
+ fileFailures.push({
90
+ file: relativeFile,
91
+ kind: "runtime-error",
92
+ reason: errorMessage(error),
93
+ });
94
+ }
95
+ },
96
+ { deadline: options.deadline, now: options.now, signal: options.signal },
97
+ );
98
+ if (outcome.kind === "timeout") {
99
+ addAnalysisTimeout(limitations, options.files.slice(index), options.displayBase);
100
+ break;
101
+ }
102
+ options.signal?.throwIfAborted();
103
+ matches.push(...fileMatches);
104
+ failures.push(...fileFailures);
105
+ if (fileFailures.length === 0) analyzedFileCount += 1;
106
+ }
107
+
108
+ if (failures.length > 0) {
109
+ limitations.push({
110
+ reason: "provider-failure",
111
+ pathCount: failures.length,
112
+ examples: failures.slice(0, 5).map((failure) => failure.file),
113
+ });
114
+ }
115
+ return { matches, failures, limitations, analyzedFileCount };
116
+ }
117
+
118
+ function addAnalysisTimeout(
119
+ limitations: StructuredScanLimitation[],
120
+ remainingFiles: readonly string[],
121
+ cwd: string,
122
+ ): void {
123
+ if (limitations.some((limitation) => limitation.reason === "timeout")) return;
124
+ limitations.push({
125
+ reason: "timeout",
126
+ pathCount: remainingFiles.length,
127
+ examples: remainingFiles.slice(0, 5).map((file) => relativeDisplayPath(cwd, file)),
128
+ });
129
+ }
130
+
131
+ // biome-ignore lint/complexity/useMaxParams: helper takes explicit collection inputs to avoid intermediate objects in the hot path
132
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: kind-specific tree-sitter matching is clearest as one helper
133
+ async function collectMatchesForFile(
134
+ matches: StructuredMatch[],
135
+ failures: StructuredFailure[],
136
+ structural: StructuralSubstrate,
137
+ relFile: string,
138
+ kind: CodeFindAstKind,
139
+ matcher: (value: string) => boolean,
140
+ ): Promise<void> {
141
+ const recordFailure = (kind: StructuredFailureKind, reason: string) => {
142
+ failures.push({ file: relFile, kind, reason });
143
+ };
144
+
145
+ if (kind === "definition") {
146
+ const outline = await structural.outline(relFile);
147
+ if (!handleStructuralResult(outline, recordFailure)) return;
148
+ for (const item of flattenOutlineItems(outline.data)) {
149
+ if (!matcher(item.name)) continue;
150
+ matches.push({ file: relFile, name: item.name, kind: item.kind, line: item.startLine });
151
+ }
152
+ return;
153
+ }
154
+
155
+ if (kind === "export") {
156
+ const exportsResult = await structural.exports(relFile);
157
+ if (!handleStructuralResult(exportsResult, recordFailure)) return;
158
+ for (const item of exportsResult.data) {
159
+ if (!matcher(item.name)) continue;
160
+ matches.push({ file: relFile, name: item.name, kind: item.kind, line: item.startLine });
161
+ }
162
+ return;
163
+ }
164
+
165
+ if (kind === "import") {
166
+ const importsResult = await structural.imports(relFile);
167
+ if (!handleStructuralResult(importsResult, recordFailure)) return;
168
+ for (const item of importsResult.data) {
169
+ if (!matcher(item.moduleSpecifier)) continue;
170
+ matches.push({
171
+ file: relFile,
172
+ name: item.moduleSpecifier,
173
+ kind: "import",
174
+ line: item.startLine,
175
+ });
176
+ }
177
+ return;
178
+ }
179
+
180
+ if (kind === "call") {
181
+ const callResult = await structural.callSites(relFile);
182
+ if (!handleStructuralResult(callResult, recordFailure)) return;
183
+ for (const call of callResult.data) {
184
+ if (!matcher(callableExpressionForMatching(call.name))) continue;
185
+ matches.push({ file: relFile, name: call.name, kind: "call", line: call.startLine });
186
+ }
187
+ return;
188
+ }
189
+
190
+ const outline = await structural.outline(relFile);
191
+ if (!handleStructuralResult(outline, recordFailure)) return;
192
+ for (const item of flattenOutlineItems(outline.data)) {
193
+ if (kind === "type" && !TYPE_KIND.test(item.kind.toLowerCase())) continue;
194
+ if (kind === "interface" && item.kind.toLowerCase() !== "interface") continue;
195
+ if (kind === "class" && item.kind.toLowerCase() !== "class") continue;
196
+ if (kind === "method" && item.kind.toLowerCase() !== "method") continue;
197
+ if (kind === "enum" && item.kind.toLowerCase() !== "enum") continue;
198
+ if (!matcher(item.name)) continue;
199
+ matches.push({ file: relFile, name: item.name, kind: item.kind, line: item.startLine });
200
+ }
201
+ }
202
+
203
+ /** Flatten provider outlines so nested class/interface/enum declarations remain searchable. */
204
+ function flattenOutlineItems(items: readonly OutlineData[]): OutlineData[] {
205
+ return items.flatMap((item) => [item, ...flattenOutlineItems(item.children ?? [])]);
206
+ }
207
+
208
+ function handleStructuralResult<T>(
209
+ result: CodeResult<T>,
210
+ recordFailure: (kind: StructuredFailureKind, reason: string) => void,
211
+ ): result is { kind: "success"; data: T } {
212
+ if (result.kind === "success") return true;
213
+ recordFailure(result.kind, result.message);
214
+ return false;
215
+ }
216
+
217
+ function createStructuredMatcher(pattern: string): (value: string) => boolean {
218
+ const ignoreCase = !/[A-Z]/.test(pattern);
219
+ const needle = ignoreCase ? pattern.toLowerCase() : pattern;
220
+ return (value: string) => {
221
+ const haystack = ignoreCase ? value.toLowerCase() : value;
222
+ return haystack.includes(needle);
223
+ };
224
+ }
225
+
226
+ function errorMessage(error: unknown): string {
227
+ return error instanceof Error ? error.message : "Structural provider failed.";
228
+ }
229
+
230
+ const TYPE_KIND = /^(?:class|interface|type|enum|struct|union|record|object|concept)$/;
@@ -1,51 +1,33 @@
1
- import type {
2
- CodeResult,
3
- OutlineData,
4
- StructuralProvider as StructuralSubstrate,
5
- } from "@mrclrchtr/supi-code-runtime/api";
1
+ import type { StructuralProvider as StructuralSubstrate } from "@mrclrchtr/supi-code-runtime/api";
6
2
  import type { StructuralSearchOperation } from "@mrclrchtr/supi-tree-sitter/api";
7
3
  import type { CodeFindAstKind } from "../../tool/find/ast-kinds.ts";
8
4
  import type { EvidencePartialReason } from "../evidence.ts";
9
5
  import {
10
6
  type AstScanExclusion,
11
- type AstScanLimitation,
12
7
  type AstScanOperations,
13
8
  type AstScanPolicy,
14
9
  DEFAULT_AST_SCAN_MAX_FILES,
15
10
  DEFAULT_AST_SCAN_TIMEOUT_MS,
16
11
  enumerateAstFiles,
17
12
  } from "./ast-scan.ts";
18
- import { callableExpressionForMatching } from "./call-name.ts";
19
- import { settleByDeadline } from "./deadline.ts";
13
+ import { startAstScanTimer } from "./ast-scan-timing.ts";
20
14
  import { relativeDisplayPath } from "./paths.ts";
21
-
22
- export interface StructuredPatternParams {
23
- readonly pattern: string;
24
- readonly kind: CodeFindAstKind;
25
- }
26
-
27
- export interface StructuredMatch {
28
- readonly file: string;
29
- readonly name: string;
30
- readonly kind: string;
31
- readonly line: number;
32
- }
33
-
34
- export type StructuredFailureKind = Exclude<CodeResult<never>, { kind: "success" }>["kind"];
35
-
36
- export interface StructuredFailure {
37
- readonly file: string;
38
- readonly kind: StructuredFailureKind;
39
- readonly reason: string;
40
- }
41
-
42
- export type StructuredScanLimitation = AstScanLimitation | ProviderScanLimitation;
43
-
44
- export interface ProviderScanLimitation {
45
- readonly reason: "provider-failure";
46
- readonly pathCount: number;
47
- readonly examples: readonly string[];
48
- }
15
+ import {
16
+ analyzeStructuredFiles,
17
+ type StructuredFailure,
18
+ type StructuredMatch,
19
+ type StructuredPatternParams,
20
+ type StructuredScanLimitation,
21
+ } from "./pattern-analysis.ts";
22
+
23
+ export type {
24
+ ProviderScanLimitation,
25
+ StructuredFailure,
26
+ StructuredFailureKind,
27
+ StructuredMatch,
28
+ StructuredPatternParams,
29
+ StructuredScanLimitation,
30
+ } from "./pattern-analysis.ts";
49
31
 
50
32
  /** Structured completeness state for the AST source-file scan. */
51
33
  export interface StructuredScanSummary {
@@ -98,6 +80,7 @@ export interface StructuredPatternSearchOptions {
98
80
  export async function getStructuredPatternMatches(
99
81
  options: StructuredPatternSearchOptions,
100
82
  ): Promise<StructuredPatternOutcome> {
83
+ const scanTimer = startAstScanTimer();
101
84
  const now = options.control?.now ?? Date.now;
102
85
  const maxFiles = options.control?.maxFiles ?? DEFAULT_AST_SCAN_MAX_FILES;
103
86
  const timeoutMs = options.control?.timeoutMs ?? DEFAULT_AST_SCAN_TIMEOUT_MS;
@@ -143,6 +126,7 @@ export async function getStructuredPatternMatches(
143
126
  };
144
127
  }
145
128
 
129
+ scanTimer.enumerationCompleted();
146
130
  const analysis = await analyzeStructuredFiles({
147
131
  files: enumeration.files,
148
132
  displayBase: enumeration.displayBase,
@@ -156,13 +140,14 @@ export async function getStructuredPatternMatches(
156
140
  const capabilityMismatches = analysis.failures.filter(
157
141
  (failure) => failure.kind === "unsupported-language",
158
142
  );
143
+ const complete = enumeration.complete && analysis.limitations.length === 0;
144
+ scanTimer.record({ context: options, operation, roots, enumeration, analysis, complete });
159
145
  if (capabilityMismatches.length > 0) {
160
146
  return {
161
147
  kind: "unavailable",
162
148
  reason: `Structural provider rejected ${capabilityMismatches.length} file${capabilityMismatches.length === 1 ? "" : "s"} declared eligible for ${operation} analysis.`,
163
149
  };
164
150
  }
165
- const complete = enumeration.complete && analysis.limitations.length === 0;
166
151
  return {
167
152
  kind: "completed",
168
153
  result: {
@@ -183,92 +168,6 @@ export async function getStructuredPatternMatches(
183
168
  };
184
169
  }
185
170
 
186
- interface AnalyzeStructuredFilesOptions {
187
- readonly files: readonly string[];
188
- readonly displayBase: string;
189
- readonly params: StructuredPatternParams;
190
- readonly structural: StructuralSubstrate;
191
- readonly deadline: number;
192
- readonly now: () => number;
193
- readonly signal?: AbortSignal;
194
- readonly initialLimitations: readonly AstScanLimitation[];
195
- }
196
-
197
- async function analyzeStructuredFiles(options: AnalyzeStructuredFilesOptions): Promise<{
198
- matches: StructuredMatch[];
199
- failures: StructuredFailure[];
200
- limitations: StructuredScanLimitation[];
201
- analyzedFileCount: number;
202
- }> {
203
- const matches: StructuredMatch[] = [];
204
- const failures: StructuredFailure[] = [];
205
- const limitations: StructuredScanLimitation[] = [...options.initialLimitations];
206
- const matcher = createStructuredMatcher(options.params.pattern);
207
- let analyzedFileCount = 0;
208
-
209
- for (const [index, absoluteFile] of options.files.entries()) {
210
- options.signal?.throwIfAborted();
211
- if (options.now() > options.deadline) {
212
- addAnalysisTimeout(limitations, options.files.slice(index), options.displayBase);
213
- break;
214
- }
215
- const relativeFile = relativeDisplayPath(options.displayBase, absoluteFile);
216
- const fileMatches: StructuredMatch[] = [];
217
- const fileFailures: StructuredFailure[] = [];
218
- const outcome = await settleByDeadline(
219
- async () => {
220
- try {
221
- await collectMatchesForFile(
222
- fileMatches,
223
- fileFailures,
224
- options.structural,
225
- relativeFile,
226
- options.params.kind,
227
- matcher,
228
- );
229
- } catch (error) {
230
- fileFailures.push({
231
- file: relativeFile,
232
- kind: "runtime-error",
233
- reason: errorMessage(error),
234
- });
235
- }
236
- },
237
- { deadline: options.deadline, now: options.now, signal: options.signal },
238
- );
239
- if (outcome.kind === "timeout") {
240
- addAnalysisTimeout(limitations, options.files.slice(index), options.displayBase);
241
- break;
242
- }
243
- options.signal?.throwIfAborted();
244
- matches.push(...fileMatches);
245
- failures.push(...fileFailures);
246
- if (fileFailures.length === 0) analyzedFileCount += 1;
247
- }
248
-
249
- if (failures.length > 0) {
250
- limitations.push({
251
- reason: "provider-failure",
252
- pathCount: failures.length,
253
- examples: failures.slice(0, 5).map((failure) => failure.file),
254
- });
255
- }
256
- return { matches, failures, limitations, analyzedFileCount };
257
- }
258
-
259
- function addAnalysisTimeout(
260
- limitations: StructuredScanLimitation[],
261
- remainingFiles: readonly string[],
262
- cwd: string,
263
- ): void {
264
- if (limitations.some((limitation) => limitation.reason === "timeout")) return;
265
- limitations.push({
266
- reason: "timeout",
267
- pathCount: remainingFiles.length,
268
- examples: remainingFiles.slice(0, 5).map((file) => relativeDisplayPath(cwd, file)),
269
- });
270
- }
271
-
272
171
  function primaryPartialReason(
273
172
  limitations: readonly StructuredScanLimitation[],
274
173
  ): EvidencePartialReason {
@@ -282,94 +181,6 @@ function primaryPartialReason(
282
181
  return "provider-limited";
283
182
  }
284
183
 
285
- // biome-ignore lint/complexity/useMaxParams: helper takes explicit collection inputs to avoid intermediate objects in the hot path
286
- // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: kind-specific tree-sitter matching is clearest as one helper
287
- async function collectMatchesForFile(
288
- matches: StructuredMatch[],
289
- failures: StructuredFailure[],
290
- structural: StructuralSubstrate,
291
- relFile: string,
292
- kind: CodeFindAstKind,
293
- matcher: (value: string) => boolean,
294
- ): Promise<void> {
295
- const recordFailure = (kind: StructuredFailureKind, reason: string) => {
296
- failures.push({ file: relFile, kind, reason });
297
- };
298
-
299
- if (kind === "definition") {
300
- const outline = await structural.outline(relFile);
301
- if (!handleStructuralResult(outline, recordFailure)) return;
302
- for (const item of flattenOutlineItems(outline.data)) {
303
- if (!matcher(item.name)) continue;
304
- matches.push({ file: relFile, name: item.name, kind: item.kind, line: item.startLine });
305
- }
306
- return;
307
- }
308
-
309
- if (kind === "export") {
310
- const exportsResult = await structural.exports(relFile);
311
- if (!handleStructuralResult(exportsResult, recordFailure)) return;
312
- for (const item of exportsResult.data) {
313
- if (!matcher(item.name)) continue;
314
- matches.push({ file: relFile, name: item.name, kind: item.kind, line: item.startLine });
315
- }
316
- return;
317
- }
318
-
319
- if (kind === "import") {
320
- const importsResult = await structural.imports(relFile);
321
- if (!handleStructuralResult(importsResult, recordFailure)) return;
322
- for (const item of importsResult.data) {
323
- if (!matcher(item.moduleSpecifier)) continue;
324
- matches.push({
325
- file: relFile,
326
- name: item.moduleSpecifier,
327
- kind: "import",
328
- line: item.startLine,
329
- });
330
- }
331
- return;
332
- }
333
-
334
- if (kind === "call") {
335
- const callResult = await structural.callSites(relFile);
336
- if (!handleStructuralResult(callResult, recordFailure)) return;
337
- for (const call of callResult.data) {
338
- if (!matcher(callableExpressionForMatching(call.name))) continue;
339
- matches.push({ file: relFile, name: call.name, kind: "call", line: call.startLine });
340
- }
341
- return;
342
- }
343
-
344
- const outline = await structural.outline(relFile);
345
- if (!handleStructuralResult(outline, recordFailure)) return;
346
- for (const item of flattenOutlineItems(outline.data)) {
347
- if (kind === "type" && !TYPE_KIND.test(item.kind.toLowerCase())) continue;
348
- if (kind === "interface" && item.kind.toLowerCase() !== "interface") continue;
349
- if (kind === "class" && item.kind.toLowerCase() !== "class") continue;
350
- if (kind === "method" && item.kind.toLowerCase() !== "method") continue;
351
- if (kind === "enum" && item.kind.toLowerCase() !== "enum") continue;
352
- if (!matcher(item.name)) continue;
353
- matches.push({ file: relFile, name: item.name, kind: item.kind, line: item.startLine });
354
- }
355
- }
356
-
357
- /** Flatten provider outlines so nested class/interface/enum declarations remain searchable. */
358
- function flattenOutlineItems(items: readonly OutlineData[]): OutlineData[] {
359
- return items.flatMap((item) => [item, ...flattenOutlineItems(item.children ?? [])]);
360
- }
361
-
362
- function handleStructuralResult<T>(
363
- result: CodeResult<T>,
364
- recordFailure: (kind: StructuredFailureKind, reason: string) => void,
365
- ): result is { kind: "success"; data: T } {
366
- if (result.kind === "success") return true;
367
- recordFailure(result.kind, result.message);
368
- return false;
369
- }
370
-
371
- const TYPE_KIND = /^(?:class|interface|type|enum|struct|union|record|object|concept)$/;
372
-
373
184
  const AST_KIND_OPERATIONS = {
374
185
  definition: "outline",
375
186
  import: "imports",
@@ -385,16 +196,3 @@ const AST_KIND_OPERATIONS = {
385
196
  function structuralOperationForKind(kind: CodeFindAstKind): StructuralSearchOperation {
386
197
  return AST_KIND_OPERATIONS[kind];
387
198
  }
388
-
389
- function createStructuredMatcher(pattern: string): (value: string) => boolean {
390
- const ignoreCase = !/[A-Z]/.test(pattern);
391
- const needle = ignoreCase ? pattern.toLowerCase() : pattern;
392
- return (value: string) => {
393
- const haystack = ignoreCase ? value.toLowerCase() : value;
394
- return haystack.includes(needle);
395
- };
396
- }
397
-
398
- function errorMessage(error: unknown): string {
399
- return error instanceof Error ? error.message : "Structural provider failed.";
400
- }