lsp-intelligence 0.2.0 → 0.2.1
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/README.md +41 -13
- package/dist/analysis/pattern/collectSearchFiles.d.ts +2 -1
- package/dist/analysis/pattern/collectSearchFiles.js +6 -6
- package/dist/analysis/pattern/collectSearchFiles.js.map +1 -1
- package/dist/analysis/pattern/runPatternSearch.d.ts +1 -1
- package/dist/analysis/pattern/runPatternSearch.js +4 -2
- package/dist/analysis/pattern/runPatternSearch.js.map +1 -1
- package/dist/analysis/ts/extractUsages.js +19 -0
- package/dist/analysis/ts/extractUsages.js.map +1 -1
- package/dist/analysis/ts/parseSourceFile.d.ts +2 -1
- package/dist/analysis/ts/parseSourceFile.js +5 -9
- package/dist/analysis/ts/parseSourceFile.js.map +1 -1
- package/dist/analysis/ts/reactState.d.ts +9 -0
- package/dist/analysis/ts/reactState.js +57 -0
- package/dist/analysis/ts/reactState.js.map +1 -0
- package/dist/analysis/ts/structuralPredicates.js +31 -2
- package/dist/analysis/ts/structuralPredicates.js.map +1 -1
- package/dist/ast/parseFile.d.ts +4 -3
- package/dist/ast/parseFile.js +22 -5
- package/dist/ast/parseFile.js.map +1 -1
- package/dist/engine/LspEngine.js +23 -1
- package/dist/engine/LspEngine.js.map +1 -1
- package/dist/git/getBaseFileContent.d.ts +1 -0
- package/dist/git/getBaseFileContent.js +10 -3
- package/dist/git/getBaseFileContent.js.map +1 -1
- package/dist/git/getChangedFiles.d.ts +2 -1
- package/dist/git/getChangedFiles.js +11 -5
- package/dist/git/getChangedFiles.js.map +1 -1
- package/dist/git/getChangedHunks.d.ts +1 -0
- package/dist/git/getChangedHunks.js +15 -10
- package/dist/git/getChangedHunks.js.map +1 -1
- package/dist/git/getGitRoot.d.ts +15 -0
- package/dist/git/getGitRoot.js +33 -0
- package/dist/git/getGitRoot.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/resolve/searchScope.d.ts +8 -2
- package/dist/resolve/searchScope.js +26 -14
- package/dist/resolve/searchScope.js.map +1 -1
- package/dist/search/adapters/config.d.ts +5 -0
- package/dist/search/adapters/config.js +56 -0
- package/dist/search/adapters/config.js.map +1 -0
- package/dist/search/adapters/http.d.ts +6 -0
- package/dist/search/adapters/http.js +58 -0
- package/dist/search/adapters/http.js.map +1 -0
- package/dist/search/adapters/react.d.ts +12 -0
- package/dist/search/adapters/react.js +92 -0
- package/dist/search/adapters/react.js.map +1 -0
- package/dist/search/adapters/registry.d.ts +6 -0
- package/dist/search/adapters/registry.js +20 -0
- package/dist/search/adapters/registry.js.map +1 -0
- package/dist/search/adapters/types.d.ts +5 -0
- package/dist/search/adapters/types.js +2 -0
- package/dist/search/adapters/types.js.map +1 -0
- package/dist/search/debug/trace.d.ts +6 -0
- package/dist/search/debug/trace.js +25 -0
- package/dist/search/debug/trace.js.map +1 -0
- package/dist/search/expand/graphExpansion.d.ts +16 -0
- package/dist/search/expand/graphExpansion.js +168 -0
- package/dist/search/expand/graphExpansion.js.map +1 -0
- package/dist/search/fileKinds.d.ts +13 -0
- package/dist/search/fileKinds.js +74 -0
- package/dist/search/fileKinds.js.map +1 -0
- package/dist/search/index/configIndex.d.ts +6 -0
- package/dist/search/index/configIndex.js +196 -0
- package/dist/search/index/configIndex.js.map +1 -0
- package/dist/search/index/docIndex.d.ts +7 -0
- package/dist/search/index/docIndex.js +116 -0
- package/dist/search/index/docIndex.js.map +1 -0
- package/dist/search/index/workspaceIndex.d.ts +6 -0
- package/dist/search/index/workspaceIndex.js +50 -14
- package/dist/search/index/workspaceIndex.js.map +1 -1
- package/dist/search/query/compileEffectiveSearchSpec.d.ts +24 -0
- package/dist/search/query/compileEffectiveSearchSpec.js +67 -0
- package/dist/search/query/compileEffectiveSearchSpec.js.map +1 -0
- package/dist/search/query/parseQuery.d.ts +1 -0
- package/dist/search/query/parseQuery.js +40 -0
- package/dist/search/query/parseQuery.js.map +1 -1
- package/dist/search/query/phraseRules.d.ts +10 -0
- package/dist/search/query/phraseRules.js +58 -0
- package/dist/search/query/phraseRules.js.map +1 -0
- package/dist/search/query/planQuery.d.ts +4 -3
- package/dist/search/query/planQuery.js +39 -6
- package/dist/search/query/planQuery.js.map +1 -1
- package/dist/search/ranking/assessConfidence.js +6 -0
- package/dist/search/ranking/assessConfidence.js.map +1 -1
- package/dist/search/ranking/candidateIdentity.d.ts +21 -0
- package/dist/search/ranking/candidateIdentity.js +29 -0
- package/dist/search/ranking/candidateIdentity.js.map +1 -0
- package/dist/search/ranking/coalesceCandidates.d.ts +9 -0
- package/dist/search/ranking/coalesceCandidates.js +48 -0
- package/dist/search/ranking/coalesceCandidates.js.map +1 -0
- package/dist/search/ranking/fieldedTextRanker.d.ts +26 -0
- package/dist/search/ranking/fieldedTextRanker.js +79 -0
- package/dist/search/ranking/fieldedTextRanker.js.map +1 -0
- package/dist/search/ranking/mergeCandidates.d.ts +3 -0
- package/dist/search/ranking/mergeCandidates.js +39 -0
- package/dist/search/ranking/mergeCandidates.js.map +1 -1
- package/dist/search/ranking/rankCandidates.d.ts +1 -1
- package/dist/search/ranking/rankCandidates.js +51 -3
- package/dist/search/ranking/rankCandidates.js.map +1 -1
- package/dist/search/retrievers/behaviorRetriever.d.ts +3 -3
- package/dist/search/retrievers/behaviorRetriever.js +64 -69
- package/dist/search/retrievers/behaviorRetriever.js.map +1 -1
- package/dist/search/retrievers/configRetriever.d.ts +7 -0
- package/dist/search/retrievers/configRetriever.js +46 -0
- package/dist/search/retrievers/configRetriever.js.map +1 -0
- package/dist/search/retrievers/docRetriever.d.ts +7 -0
- package/dist/search/retrievers/docRetriever.js +46 -0
- package/dist/search/retrievers/docRetriever.js.map +1 -0
- package/dist/search/retrievers/identifierRetriever.d.ts +3 -3
- package/dist/search/retrievers/identifierRetriever.js +7 -6
- package/dist/search/retrievers/identifierRetriever.js.map +1 -1
- package/dist/search/retrievers/structuralRetriever.d.ts +4 -6
- package/dist/search/retrievers/structuralRetriever.js +47 -84
- package/dist/search/retrievers/structuralRetriever.js.map +1 -1
- package/dist/search/retrievers/textPatternRetriever.d.ts +7 -0
- package/dist/search/retrievers/textPatternRetriever.js +90 -0
- package/dist/search/retrievers/textPatternRetriever.js.map +1 -0
- package/dist/search/structural/locators/callLocator.d.ts +6 -0
- package/dist/search/structural/locators/callLocator.js +57 -0
- package/dist/search/structural/locators/callLocator.js.map +1 -0
- package/dist/search/structural/locators/declarationLocator.d.ts +6 -0
- package/dist/search/structural/locators/declarationLocator.js +57 -0
- package/dist/search/structural/locators/declarationLocator.js.map +1 -0
- package/dist/search/structural/locators/statementLocator.d.ts +6 -0
- package/dist/search/structural/locators/statementLocator.js +65 -0
- package/dist/search/structural/locators/statementLocator.js.map +1 -0
- package/dist/search/structural/locators/types.d.ts +12 -0
- package/dist/search/structural/locators/types.js +2 -0
- package/dist/search/structural/locators/types.js.map +1 -0
- package/dist/search/structural/selectLocators.d.ts +7 -0
- package/dist/search/structural/selectLocators.js +21 -0
- package/dist/search/structural/selectLocators.js.map +1 -0
- package/dist/search/types.d.ts +77 -7
- package/dist/search/types.js.map +1 -1
- package/dist/tools/composites/apiGuard.js +6 -28
- package/dist/tools/composites/apiGuard.js.map +1 -1
- package/dist/tools/composites/findCode.js +92 -15
- package/dist/tools/composites/findCode.js.map +1 -1
- package/dist/tools/composites/findPattern.js +1 -1
- package/dist/tools/composites/findPattern.js.map +1 -1
- package/dist/tools/live/clearIndex.d.ts +1 -0
- package/dist/tools/live/clearIndex.js +19 -0
- package/dist/tools/live/clearIndex.js.map +1 -0
- package/dist/tools/primitives/callHierarchy.js +1 -1
- package/dist/tools/primitives/callHierarchy.js.map +1 -1
- package/package.json +4 -2
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
import { parseSourceFile } from '../../analysis/ts/parseSourceFile.js';
|
|
2
2
|
import { evaluateStructuralPredicates } from '../../analysis/ts/structuralPredicates.js';
|
|
3
3
|
import { buildSnippetFromFile } from '../../analysis/ts/snippets.js';
|
|
4
|
+
import { selectLocators } from '../structural/selectLocators.js';
|
|
4
5
|
import ts from 'typescript';
|
|
5
6
|
/**
|
|
6
7
|
* Retrieve candidates using structural predicate evaluation.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Step 2: Evaluate structural predicates on each candidate node
|
|
11
|
-
*
|
|
12
|
-
* This is NOT recipe-only string patterns — it uses TS AST node evaluation.
|
|
9
|
+
* When an EffectiveSearchSpec is provided, uses its merged predicates/identifiers
|
|
10
|
+
* (which include recipe contributions). Falls back to raw IR otherwise.
|
|
13
11
|
*/
|
|
14
|
-
export function retrieveStructuralCandidates(ir, scope, index) {
|
|
15
|
-
|
|
12
|
+
export function retrieveStructuralCandidates(ir, scope, index, spec) {
|
|
13
|
+
const predicates = spec?.structuralPredicates ?? ir.structuralPredicates;
|
|
14
|
+
const exactIds = spec?.exactIdentifiers ?? ir.exactIdentifiers;
|
|
15
|
+
const dottedIds = spec?.dottedIdentifiers ?? ir.dottedIdentifiers;
|
|
16
|
+
if (predicates.length === 0)
|
|
16
17
|
return [];
|
|
17
18
|
const candidates = [];
|
|
18
19
|
const maxFiles = 80;
|
|
19
20
|
let filesChecked = 0;
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const locators = selectLocators(ir);
|
|
22
|
+
if (locators.length === 0)
|
|
23
|
+
return [];
|
|
24
|
+
// Determine which files to check
|
|
22
25
|
const targetFiles = new Set();
|
|
23
|
-
if (
|
|
24
|
-
|
|
26
|
+
if (exactIds.length > 0 || dottedIds.length > 0) {
|
|
27
|
+
// If we have identifiers, only check files with matching usages
|
|
28
|
+
const allIds = [...exactIds, ...dottedIds];
|
|
25
29
|
for (const usage of index.usages) {
|
|
26
30
|
if (allIds.some((id) => usage.identifier === id || usage.normalizedIdentifier === id)) {
|
|
27
31
|
targetFiles.add(usage.filePath);
|
|
@@ -29,14 +33,14 @@ export function retrieveStructuralCandidates(ir, scope, index) {
|
|
|
29
33
|
}
|
|
30
34
|
}
|
|
31
35
|
else {
|
|
32
|
-
// No specific identifier — check
|
|
36
|
+
// No specific identifier — check indexed files (capped)
|
|
33
37
|
for (const file of index.files.keys()) {
|
|
34
38
|
targetFiles.add(file);
|
|
35
39
|
if (targetFiles.size >= maxFiles)
|
|
36
40
|
break;
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
|
-
//
|
|
43
|
+
// For each file, run all selected locators and evaluate predicates
|
|
40
44
|
for (const filePath of targetFiles) {
|
|
41
45
|
if (filesChecked >= maxFiles)
|
|
42
46
|
break;
|
|
@@ -44,80 +48,39 @@ export function retrieveStructuralCandidates(ir, scope, index) {
|
|
|
44
48
|
const sf = parseSourceFile(filePath);
|
|
45
49
|
if (!sf)
|
|
46
50
|
continue;
|
|
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
|
-
return candidates.sort((a, b) => b.score - a.score);
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Find call expression nodes that match the target identifiers.
|
|
83
|
-
* For queries without identifiers, returns all top-level call expressions.
|
|
84
|
-
*/
|
|
85
|
-
function findTargetCallNodes(sf, identifiers) {
|
|
86
|
-
const nodes = [];
|
|
87
|
-
function visit(node) {
|
|
88
|
-
if (ts.isCallExpression(node)) {
|
|
89
|
-
const expr = node.expression;
|
|
90
|
-
let name = null;
|
|
91
|
-
if (ts.isIdentifier(expr)) {
|
|
92
|
-
name = expr.text;
|
|
93
|
-
}
|
|
94
|
-
else if (ts.isPropertyAccessExpression(expr)) {
|
|
95
|
-
name = getFullPropertyAccess(expr);
|
|
96
|
-
}
|
|
97
|
-
if (name) {
|
|
98
|
-
const matches = identifiers.length === 0 ||
|
|
99
|
-
identifiers.some((id) => name === id || name.endsWith(`.${id}`));
|
|
100
|
-
if (matches) {
|
|
101
|
-
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
102
|
-
nodes.push({ node, identifier: name, line });
|
|
103
|
-
}
|
|
51
|
+
for (const locator of locators) {
|
|
52
|
+
const nodes = locator.locate(sf, ir);
|
|
53
|
+
for (const { node, identifier, line } of nodes) {
|
|
54
|
+
const { matched, evidence } = evaluateStructuralPredicates(sf, node, predicates);
|
|
55
|
+
if (matched.length === 0)
|
|
56
|
+
continue;
|
|
57
|
+
const score = matched.length * 5 + (matched.length === predicates.length ? 5 : 0);
|
|
58
|
+
const { snippet, context } = buildSnippetFromFile(filePath, line, 2);
|
|
59
|
+
const enclosing = findEnclosingDeclaration(node);
|
|
60
|
+
candidates.push({
|
|
61
|
+
candidateType: 'usage',
|
|
62
|
+
filePath,
|
|
63
|
+
line,
|
|
64
|
+
column: sf.getLineAndCharacterOfPosition(node.getStart(sf)).character,
|
|
65
|
+
matchedIdentifier: identifier,
|
|
66
|
+
enclosingSymbol: enclosing?.name,
|
|
67
|
+
enclosingKind: enclosing?.kind,
|
|
68
|
+
kind: 'usage',
|
|
69
|
+
snippet,
|
|
70
|
+
context,
|
|
71
|
+
score,
|
|
72
|
+
evidence: [
|
|
73
|
+
`structural-match: ${matched.join(', ')}`,
|
|
74
|
+
`predicates: ${matched.length}/${ir.structuralPredicates.length}`,
|
|
75
|
+
`locator: ${locator.kind}`,
|
|
76
|
+
...evidence,
|
|
77
|
+
],
|
|
78
|
+
sources: ['structural'],
|
|
79
|
+
});
|
|
104
80
|
}
|
|
105
81
|
}
|
|
106
|
-
ts.forEachChild(node, visit);
|
|
107
82
|
}
|
|
108
|
-
|
|
109
|
-
return nodes;
|
|
110
|
-
}
|
|
111
|
-
function getFullPropertyAccess(node) {
|
|
112
|
-
const parts = [node.name.text];
|
|
113
|
-
let current = node.expression;
|
|
114
|
-
while (ts.isPropertyAccessExpression(current)) {
|
|
115
|
-
parts.unshift(current.name.text);
|
|
116
|
-
current = current.expression;
|
|
117
|
-
}
|
|
118
|
-
if (ts.isIdentifier(current))
|
|
119
|
-
parts.unshift(current.text);
|
|
120
|
-
return parts.join('.');
|
|
83
|
+
return candidates.sort((a, b) => b.score - a.score);
|
|
121
84
|
}
|
|
122
85
|
function findEnclosingDeclaration(node) {
|
|
123
86
|
let current = node.parent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structuralRetriever.js","sourceRoot":"","sources":["../../../src/search/retrievers/structuralRetriever.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"structuralRetriever.js","sourceRoot":"","sources":["../../../src/search/retrievers/structuralRetriever.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { QueryIR, SearchScope, WorkspaceIndex, CodeCandidate } from '../types.js';
|
|
2
|
+
import type { EffectiveSearchSpec } from '../query/compileEffectiveSearchSpec.js';
|
|
3
|
+
/**
|
|
4
|
+
* Retrieve candidates by running compiled regex specs over scoped files.
|
|
5
|
+
* Uses the compiled spec's merged regexes (from all adapter recipes).
|
|
6
|
+
*/
|
|
7
|
+
export declare function retrieveTextPatternCandidates(ir: QueryIR, scope: SearchScope, index: WorkspaceIndex, spec?: EffectiveSearchSpec): CodeCandidate[];
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import { buildSnippetFromFile } from '../../analysis/ts/snippets.js';
|
|
3
|
+
/**
|
|
4
|
+
* Retrieve candidates by running compiled regex specs over scoped files.
|
|
5
|
+
* Uses the compiled spec's merged regexes (from all adapter recipes).
|
|
6
|
+
*/
|
|
7
|
+
export function retrieveTextPatternCandidates(ir, scope, index, spec) {
|
|
8
|
+
// Use compiled spec regexes if available, otherwise collect from raw recipes
|
|
9
|
+
const regexSpecs = spec?.regexes ?? [];
|
|
10
|
+
if (regexSpecs.length === 0) {
|
|
11
|
+
for (const recipe of ir.recipes) {
|
|
12
|
+
if (recipe.regexes)
|
|
13
|
+
regexSpecs.push(...recipe.regexes);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (regexSpecs.length === 0)
|
|
17
|
+
return [];
|
|
18
|
+
const compiled = regexSpecs.map((spec) => ({
|
|
19
|
+
spec,
|
|
20
|
+
regex: new RegExp(spec.pattern, spec.flags ?? 'g'),
|
|
21
|
+
}));
|
|
22
|
+
const candidates = [];
|
|
23
|
+
const maxFiles = 200;
|
|
24
|
+
let filesChecked = 0;
|
|
25
|
+
for (const [filePath] of index.files) {
|
|
26
|
+
if (filesChecked >= maxFiles)
|
|
27
|
+
break;
|
|
28
|
+
filesChecked++;
|
|
29
|
+
let content;
|
|
30
|
+
try {
|
|
31
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const lines = content.split('\n');
|
|
37
|
+
for (const { spec, regex } of compiled) {
|
|
38
|
+
// Reset regex state
|
|
39
|
+
regex.lastIndex = 0;
|
|
40
|
+
let match;
|
|
41
|
+
while ((match = regex.exec(content)) !== null) {
|
|
42
|
+
// Find the line number of this match
|
|
43
|
+
let charCount = 0;
|
|
44
|
+
let lineNum = 1;
|
|
45
|
+
for (let i = 0; i < lines.length; i++) {
|
|
46
|
+
charCount += lines[i].length + 1; // +1 for newline
|
|
47
|
+
if (charCount > match.index) {
|
|
48
|
+
lineNum = i + 1;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const { snippet, context } = buildSnippetFromFile(filePath, lineNum, 1);
|
|
53
|
+
const enclosing = findEnclosingFunctionName(lines, lineNum - 1);
|
|
54
|
+
candidates.push({
|
|
55
|
+
candidateType: 'pattern',
|
|
56
|
+
filePath,
|
|
57
|
+
line: lineNum,
|
|
58
|
+
symbol: enclosing,
|
|
59
|
+
matchedIdentifier: spec.id,
|
|
60
|
+
kind: 'pattern',
|
|
61
|
+
snippet,
|
|
62
|
+
context,
|
|
63
|
+
score: 6, // Base score for regex match
|
|
64
|
+
evidence: [`regex-match: ${spec.id}`, `pattern: ${spec.pattern.substring(0, 60)}`],
|
|
65
|
+
sources: ['identifier'], // Counts as identifier-like evidence
|
|
66
|
+
});
|
|
67
|
+
// Limit matches per file
|
|
68
|
+
if (candidates.length > 500)
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return candidates.sort((a, b) => b.score - a.score);
|
|
74
|
+
}
|
|
75
|
+
/** Simple heuristic to find enclosing function name by scanning backwards. */
|
|
76
|
+
function findEnclosingFunctionName(lines, fromLine) {
|
|
77
|
+
for (let i = fromLine; i >= Math.max(0, fromLine - 15); i--) {
|
|
78
|
+
const line = lines[i];
|
|
79
|
+
// function foo(
|
|
80
|
+
const funcMatch = line.match(/(?:export\s+)?(?:async\s+)?function\s+(\w+)/);
|
|
81
|
+
if (funcMatch)
|
|
82
|
+
return funcMatch[1];
|
|
83
|
+
// const foo = (
|
|
84
|
+
const arrowMatch = line.match(/(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=/);
|
|
85
|
+
if (arrowMatch && (line.includes('=>') || line.includes('function')))
|
|
86
|
+
return arrowMatch[1];
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=textPatternRetriever.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textPatternRetriever.js","sourceRoot":"","sources":["../../../src/search/retrievers/textPatternRetriever.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
const CALL_PREDICATES = new Set([
|
|
3
|
+
'returns-cleanup', 'no-cleanup', 'returns-function',
|
|
4
|
+
'hook-callback', 'inside-hook', 'conditional',
|
|
5
|
+
]);
|
|
6
|
+
/**
|
|
7
|
+
* Locates call expression nodes — the original structural retriever path.
|
|
8
|
+
* Best for: useEffect, Promise.all, hook/API usage shapes.
|
|
9
|
+
*/
|
|
10
|
+
export const callLocator = {
|
|
11
|
+
kind: 'call',
|
|
12
|
+
supports(predicates, ir) {
|
|
13
|
+
// Supports if we have identifiers to match, or call-centric predicates
|
|
14
|
+
if (ir.exactIdentifiers.length > 0 || ir.dottedIdentifiers.length > 0)
|
|
15
|
+
return true;
|
|
16
|
+
return predicates.some((p) => CALL_PREDICATES.has(p));
|
|
17
|
+
},
|
|
18
|
+
locate(sf, ir) {
|
|
19
|
+
const nodes = [];
|
|
20
|
+
const allIds = [...ir.exactIdentifiers, ...ir.dottedIdentifiers];
|
|
21
|
+
function visit(node) {
|
|
22
|
+
if (ts.isCallExpression(node)) {
|
|
23
|
+
const expr = node.expression;
|
|
24
|
+
let name = null;
|
|
25
|
+
if (ts.isIdentifier(expr)) {
|
|
26
|
+
name = expr.text;
|
|
27
|
+
}
|
|
28
|
+
else if (ts.isPropertyAccessExpression(expr)) {
|
|
29
|
+
name = getFullPropertyAccess(expr);
|
|
30
|
+
}
|
|
31
|
+
if (name) {
|
|
32
|
+
const matches = allIds.length === 0 ||
|
|
33
|
+
allIds.some((id) => name === id || name.endsWith(`.${id}`));
|
|
34
|
+
if (matches) {
|
|
35
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
36
|
+
nodes.push({ node, identifier: name, line });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
ts.forEachChild(node, visit);
|
|
41
|
+
}
|
|
42
|
+
visit(sf);
|
|
43
|
+
return nodes;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
function getFullPropertyAccess(node) {
|
|
47
|
+
const parts = [node.name.text];
|
|
48
|
+
let current = node.expression;
|
|
49
|
+
while (ts.isPropertyAccessExpression(current)) {
|
|
50
|
+
parts.unshift(current.name.text);
|
|
51
|
+
current = current.expression;
|
|
52
|
+
}
|
|
53
|
+
if (ts.isIdentifier(current))
|
|
54
|
+
parts.unshift(current.text);
|
|
55
|
+
return parts.join('.');
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=callLocator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callLocator.js","sourceRoot":"","sources":["../../../../src/search/structural/locators/callLocator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAI5B,MAAM,eAAe,GAA6B,IAAI,GAAG,CAAC;IACxD,iBAAiB,EAAE,YAAY,EAAE,kBAAkB;IACnD,eAAe,EAAE,aAAa,EAAE,aAAa;CAC9C,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAsB;IAC5C,IAAI,EAAE,MAAM;IAEZ,QAAQ,CAAC,UAAU,EAAE,EAAE;QACrB,uEAAuE;QACvE,IAAI,EAAE,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACnF,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,CAAC,EAAE,EAAE,EAAE;QACX,MAAM,KAAK,GAAkB,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,CAAC;QAEjE,SAAS,KAAK,CAAC,IAAa;YAC1B,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;gBAC7B,IAAI,IAAI,GAAkB,IAAI,CAAC;gBAE/B,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC1B,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;gBACnB,CAAC;qBAAM,IAAI,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC/C,IAAI,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;gBACrC,CAAC;gBAED,IAAI,IAAI,EAAE,CAAC;oBACT,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC;wBACjC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,IAAI,IAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;oBAC/D,IAAI,OAAO,EAAE,CAAC;wBACZ,MAAM,IAAI,GAAG,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;wBAC1E,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;oBAC/C,CAAC;gBACH,CAAC;YACH,CAAC;YACD,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;QAED,KAAK,CAAC,EAAE,CAAC,CAAC;QACV,OAAO,KAAK,CAAC;IACf,CAAC;CACF,CAAC;AAEF,SAAS,qBAAqB,CAAC,IAAiC;IAC9D,MAAM,KAAK,GAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,OAAO,GAAkB,IAAI,CAAC,UAAU,CAAC;IAC7C,OAAO,EAAE,CAAC,0BAA0B,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9C,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC;IAC/B,CAAC;IACD,IAAI,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC;QAAE,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { StructuralLocator } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Locates function/method declarations for structural filtering.
|
|
4
|
+
* Best for: "async function without try/catch", declaration-shaped queries.
|
|
5
|
+
*/
|
|
6
|
+
export declare const declarationLocator: StructuralLocator;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
const DECLARATION_PREDICATES = new Set([
|
|
3
|
+
'no-try-catch', 'returns-function',
|
|
4
|
+
]);
|
|
5
|
+
/**
|
|
6
|
+
* Locates function/method declarations for structural filtering.
|
|
7
|
+
* Best for: "async function without try/catch", declaration-shaped queries.
|
|
8
|
+
*/
|
|
9
|
+
export const declarationLocator = {
|
|
10
|
+
kind: 'declaration',
|
|
11
|
+
supports(predicates, ir) {
|
|
12
|
+
// Only use when no identifiers and predicates apply to declarations
|
|
13
|
+
if (ir.exactIdentifiers.length > 0)
|
|
14
|
+
return false;
|
|
15
|
+
return predicates.some((p) => DECLARATION_PREDICATES.has(p));
|
|
16
|
+
},
|
|
17
|
+
locate(sf, ir) {
|
|
18
|
+
const nodes = [];
|
|
19
|
+
const isAsync = ir.codeTokens.includes('async');
|
|
20
|
+
function visit(node) {
|
|
21
|
+
if (ts.isFunctionDeclaration(node) && node.name && node.body) {
|
|
22
|
+
// Filter to async if query mentions async
|
|
23
|
+
if (isAsync && !node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
24
|
+
ts.forEachChild(node, visit);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
28
|
+
nodes.push({ node, identifier: node.name.text, line });
|
|
29
|
+
}
|
|
30
|
+
// Arrow functions / function expressions assigned to variables
|
|
31
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
|
|
32
|
+
const init = node.initializer;
|
|
33
|
+
if (ts.isArrowFunction(init) || ts.isFunctionExpression(init)) {
|
|
34
|
+
if (isAsync && !init.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
35
|
+
ts.forEachChild(node, visit);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
39
|
+
nodes.push({ node: init, identifier: node.name.text, line });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Methods
|
|
43
|
+
if (ts.isMethodDeclaration(node) && ts.isIdentifier(node.name) && node.body) {
|
|
44
|
+
if (isAsync && !node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
45
|
+
ts.forEachChild(node, visit);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
49
|
+
nodes.push({ node, identifier: node.name.text, line });
|
|
50
|
+
}
|
|
51
|
+
ts.forEachChild(node, visit);
|
|
52
|
+
}
|
|
53
|
+
visit(sf);
|
|
54
|
+
return nodes;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=declarationLocator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"declarationLocator.js","sourceRoot":"","sources":["../../../../src/search/structural/locators/declarationLocator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { StructuralLocator } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Locates statement-level nodes — switch, try/catch, loops with await.
|
|
4
|
+
* Best for: "switch without default", "await inside loop", "async without try/catch".
|
|
5
|
+
*/
|
|
6
|
+
export declare const statementLocator: StructuralLocator;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
const STATEMENT_PREDICATES = new Set([
|
|
3
|
+
'switch-no-default', 'has-try-catch', 'no-try-catch', 'await-in-loop',
|
|
4
|
+
]);
|
|
5
|
+
/**
|
|
6
|
+
* Locates statement-level nodes — switch, try/catch, loops with await.
|
|
7
|
+
* Best for: "switch without default", "await inside loop", "async without try/catch".
|
|
8
|
+
*/
|
|
9
|
+
export const statementLocator = {
|
|
10
|
+
kind: 'statement',
|
|
11
|
+
supports(predicates) {
|
|
12
|
+
return predicates.some((p) => STATEMENT_PREDICATES.has(p));
|
|
13
|
+
},
|
|
14
|
+
locate(sf, ir) {
|
|
15
|
+
const nodes = [];
|
|
16
|
+
const targetPredicates = ir.structuralPredicates.filter((p) => STATEMENT_PREDICATES.has(p));
|
|
17
|
+
function visit(node) {
|
|
18
|
+
// Switch statements
|
|
19
|
+
if (ts.isSwitchStatement(node) && targetPredicates.includes('switch-no-default')) {
|
|
20
|
+
const hasDefault = node.caseBlock.clauses.some((c) => ts.isDefaultClause(c));
|
|
21
|
+
if (!hasDefault) {
|
|
22
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
23
|
+
nodes.push({ node, identifier: 'switch', line });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Try statements
|
|
27
|
+
if (ts.isTryStatement(node) && targetPredicates.includes('has-try-catch')) {
|
|
28
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
29
|
+
nodes.push({ node, identifier: 'try-catch', line });
|
|
30
|
+
}
|
|
31
|
+
// Loops with await
|
|
32
|
+
if (isLoopStatement(node) && targetPredicates.includes('await-in-loop')) {
|
|
33
|
+
if (containsAwait(node)) {
|
|
34
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
35
|
+
nodes.push({ node, identifier: 'await-in-loop', line });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
ts.forEachChild(node, visit);
|
|
39
|
+
}
|
|
40
|
+
visit(sf);
|
|
41
|
+
return nodes;
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
function isLoopStatement(node) {
|
|
45
|
+
return ts.isForStatement(node) || ts.isForOfStatement(node) ||
|
|
46
|
+
ts.isForInStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node);
|
|
47
|
+
}
|
|
48
|
+
function containsAwait(node) {
|
|
49
|
+
let found = false;
|
|
50
|
+
function walk(n) {
|
|
51
|
+
if (found)
|
|
52
|
+
return;
|
|
53
|
+
if (ts.isAwaitExpression(n)) {
|
|
54
|
+
found = true;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// Don't descend into nested functions
|
|
58
|
+
if (ts.isFunctionDeclaration(n) || ts.isArrowFunction(n) || ts.isFunctionExpression(n))
|
|
59
|
+
return;
|
|
60
|
+
ts.forEachChild(n, walk);
|
|
61
|
+
}
|
|
62
|
+
ts.forEachChild(node, walk);
|
|
63
|
+
return found;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=statementLocator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statementLocator.js","sourceRoot":"","sources":["../../../../src/search/structural/locators/statementLocator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import type { QueryIR, StructuralPredicate } from '../../types.js';
|
|
3
|
+
export interface LocatedNode {
|
|
4
|
+
node: ts.Node;
|
|
5
|
+
identifier?: string;
|
|
6
|
+
line: number;
|
|
7
|
+
}
|
|
8
|
+
export interface StructuralLocator {
|
|
9
|
+
kind: 'call' | 'statement' | 'declaration';
|
|
10
|
+
supports(predicates: StructuralPredicate[], ir: QueryIR): boolean;
|
|
11
|
+
locate(sf: ts.SourceFile, ir: QueryIR): LocatedNode[];
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/search/structural/locators/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { QueryIR } from '../types.js';
|
|
2
|
+
import type { StructuralLocator } from './locators/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Select which locators to use based on the query's structural predicates.
|
|
5
|
+
* May return multiple locators if the query spans call + statement shapes.
|
|
6
|
+
*/
|
|
7
|
+
export declare function selectLocators(ir: QueryIR): StructuralLocator[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { callLocator } from './locators/callLocator.js';
|
|
2
|
+
import { statementLocator } from './locators/statementLocator.js';
|
|
3
|
+
import { declarationLocator } from './locators/declarationLocator.js';
|
|
4
|
+
const ALL_LOCATORS = [
|
|
5
|
+
callLocator,
|
|
6
|
+
statementLocator,
|
|
7
|
+
declarationLocator,
|
|
8
|
+
];
|
|
9
|
+
/**
|
|
10
|
+
* Select which locators to use based on the query's structural predicates.
|
|
11
|
+
* May return multiple locators if the query spans call + statement shapes.
|
|
12
|
+
*/
|
|
13
|
+
export function selectLocators(ir) {
|
|
14
|
+
const selected = ALL_LOCATORS.filter((l) => l.supports(ir.structuralPredicates, ir));
|
|
15
|
+
// Always include at least call locator if we have identifiers
|
|
16
|
+
if (selected.length === 0 && (ir.exactIdentifiers.length > 0 || ir.dottedIdentifiers.length > 0)) {
|
|
17
|
+
selected.push(callLocator);
|
|
18
|
+
}
|
|
19
|
+
return selected;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=selectLocators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectLocators.js","sourceRoot":"","sources":["../../../src/search/structural/selectLocators.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAEtE,MAAM,YAAY,GAAwB;IACxC,WAAW;IACX,gBAAgB;IAChB,kBAAkB;CACnB,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAAW;IACxC,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC,CAAC;IACrF,8DAA8D;IAC9D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;QACjG,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|