@mrclrchtr/supi-code-intelligence 6.3.0 → 7.0.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/README.md +164 -82
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-lsp/README.md +35 -13
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/index.js +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/legacy.js +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +16 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +43 -35
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +83 -17
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +108 -92
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +44 -82
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +3 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +9 -68
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +45 -58
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +230 -331
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +201 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +129 -70
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-typescript.ts +395 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +8 -12
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +685 -183
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +60 -60
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-request-enrollment.ts +75 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-barrier.ts +443 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-errors.ts +30 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-file.ts +15 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +290 -37
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +72 -16
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +1 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +30 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +4 -32
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sources.ts +108 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +10 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-process-crash-report.ts +50 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +11 -7
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +770 -56
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +37 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +48 -12
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +50 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callee-display-name.ts +165 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +67 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +2 -0
- package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/browser/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/package.json +1 -1
- package/package.json +5 -5
- package/src/analysis/health/diagnostics.ts +1 -2
- package/src/analysis/health/file-scope.ts +4 -1
- package/src/analysis/health/server-status.ts +1 -1
- package/src/analysis/relations/callees.ts +17 -8
- package/src/analysis/relations/implementations.ts +1 -2
- package/src/analysis/relations/references.ts +8 -4
- package/src/analysis/relations/types.ts +6 -30
- package/src/analysis/target/anchored.ts +9 -1
- package/src/analysis/target/symbol.ts +29 -11
- package/src/analysis/target/types.ts +14 -6
- package/src/api.ts +2 -0
- package/src/session/graph/collect.ts +18 -3
- package/src/session/health-refresh.ts +27 -11
- package/src/session/health-types.ts +21 -4
- package/src/session/health-workflow.ts +28 -22
- package/src/session/orientation-types.ts +5 -6
- package/src/session/orientation-workflow.ts +10 -2
- package/src/session/session.ts +12 -5
- package/src/session/target-workflow.ts +9 -6
- package/src/substrate/lsp/maintenance.ts +53 -47
- package/src/substrate/lsp/source-tracking.ts +252 -0
- package/src/tool/code_find/guidance.ts +3 -3
- package/src/tool/code_find/spec.ts +5 -4
- package/src/tool/code_find/tui.ts +7 -1
- package/src/tool/code_graph/details.ts +32 -0
- package/src/tool/code_graph/display.ts +54 -0
- package/src/tool/code_graph/format.ts +60 -0
- package/src/tool/code_graph/guidance.ts +2 -2
- package/src/tool/code_graph/markdown.ts +76 -198
- package/src/tool/code_graph/read-next.ts +65 -0
- package/src/tool/code_graph/result.ts +118 -83
- package/src/tool/code_graph/tui.ts +216 -54
- package/src/tool/code_health/guidance.ts +3 -5
- package/src/tool/code_health/markdown.ts +90 -57
- package/src/tool/code_health/refresh-outcome.ts +51 -0
- package/src/tool/code_health/refresh-status.ts +130 -43
- package/src/tool/code_health/scope.ts +78 -0
- package/src/tool/code_health/spec.ts +1 -1
- package/src/tool/code_health/tui.ts +29 -38
- package/src/tool/code_inspect/guidance.ts +5 -3
- package/src/tool/code_inspect/tui.ts +7 -1
- package/src/tool/code_orientation/guidance.ts +3 -4
- package/src/tool/code_orientation/result.ts +31 -8
- package/src/tool/code_orientation/tui.ts +26 -5
- package/src/tool/code_refactor_apply/guidance.ts +1 -1
- package/src/tool/code_refactor_apply/tui.ts +7 -3
- package/src/tool/code_refactor_plan/guidance.ts +3 -5
- package/src/tool/code_refactor_plan/tui.ts +7 -1
- package/src/tool/code_resolve/guidance.ts +3 -5
- package/src/tool/code_resolve/result.ts +13 -10
- package/src/tool/code_resolve/tui.ts +18 -1
- package/src/tool/guidance.ts +11 -9
- package/src/tool/infra/truncate.ts +39 -13
- package/src/tool/result/candidate-row.ts +27 -0
- package/src/tool/result/errors.ts +21 -6
- package/src/tool/result/types.ts +7 -3
- package/src/tool/schemas.ts +5 -0
- package/src/types/execution.ts +2 -0
- package/src/types/index.ts +2 -0
- package/src/ui/tui/candidate-legacy-body.ts +69 -0
- package/src/ui/tui/candidate-selection.ts +366 -0
- package/src/ui/tui/common.ts +26 -31
- package/src/ui/tui/execution-error.ts +92 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-tree-sitter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Structural AST analysis for SuPi code intelligence",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"jiti": "^2.7.0",
|
|
38
38
|
"web-tree-sitter": "^0.27.0",
|
|
39
|
-
"@mrclrchtr/supi-
|
|
40
|
-
"@mrclrchtr/supi-
|
|
39
|
+
"@mrclrchtr/supi-core": "7.0.0",
|
|
40
|
+
"@mrclrchtr/supi-code-runtime": "7.0.0"
|
|
41
41
|
},
|
|
42
42
|
"bundledDependencies": [
|
|
43
43
|
"@mrclrchtr/supi-code-runtime",
|
|
@@ -145,7 +145,9 @@ function mapCalleesAtResult(result: CalleesAtResult): CalleesData {
|
|
|
145
145
|
},
|
|
146
146
|
callees: result.callees.map((c) => ({
|
|
147
147
|
name: c.name,
|
|
148
|
+
...(c.displayName === undefined ? {} : { displayName: c.displayName }),
|
|
148
149
|
startLine: c.range.startLine,
|
|
150
|
+
startCharacter: c.range.startCharacter,
|
|
149
151
|
})),
|
|
150
152
|
depth: result.depth,
|
|
151
153
|
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { SyntaxNodeLike } from "../syntax-node.ts";
|
|
2
|
+
import type { GrammarId } from "../types.ts";
|
|
3
|
+
import { normalizeCallName } from "./call-name.ts";
|
|
4
|
+
|
|
5
|
+
type CalleeSyntaxChild = SyntaxNodeLike & {
|
|
6
|
+
readonly isExtra: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type CalleeSyntaxNode = SyntaxNodeLike & {
|
|
10
|
+
readonly namedChildren: CalleeSyntaxChild[];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type SourcePoint = { row: number; column: number };
|
|
14
|
+
|
|
15
|
+
type TextReplacement = { start: number; end: number };
|
|
16
|
+
|
|
17
|
+
type CalleeDisplayNameOptions = {
|
|
18
|
+
node: SyntaxNodeLike;
|
|
19
|
+
grammarId: GrammarId;
|
|
20
|
+
nodeType: string;
|
|
21
|
+
source: string;
|
|
22
|
+
sourceLineStarts: readonly number[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const CALL_NODE_TYPES = new Set([
|
|
26
|
+
"call_expression",
|
|
27
|
+
"call",
|
|
28
|
+
"method_invocation",
|
|
29
|
+
"new_expression",
|
|
30
|
+
"object_creation_expression",
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
const ARGUMENT_LIST_TYPES = new Set(["arguments", "argument_list", "value_arguments"]);
|
|
34
|
+
|
|
35
|
+
const RECEIVER_FIELDS = [
|
|
36
|
+
"function",
|
|
37
|
+
"constructor",
|
|
38
|
+
"object",
|
|
39
|
+
"receiver",
|
|
40
|
+
"operand",
|
|
41
|
+
"value",
|
|
42
|
+
"lhs",
|
|
43
|
+
"argument",
|
|
44
|
+
] as const;
|
|
45
|
+
|
|
46
|
+
const RECEIVER_WRAPPER_TYPES = new Set(["navigation_expression", "parenthesized_expression"]);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create a syntax-shortened label for one structural callee capture.
|
|
50
|
+
*
|
|
51
|
+
* The full normalized name remains the identity-bearing value. This helper
|
|
52
|
+
* only replaces argument-list nodes on the receiver chain with `(…)`; it does
|
|
53
|
+
* not inspect arbitrary expression text to find parentheses.
|
|
54
|
+
*/
|
|
55
|
+
export function createCalleeDisplayName({
|
|
56
|
+
node,
|
|
57
|
+
grammarId,
|
|
58
|
+
nodeType,
|
|
59
|
+
source,
|
|
60
|
+
sourceLineStarts,
|
|
61
|
+
}: CalleeDisplayNameOptions): string | undefined {
|
|
62
|
+
const replacements: TextReplacement[] = [];
|
|
63
|
+
collectReceiverArgumentLists(node, replacements, sourceLineStarts);
|
|
64
|
+
const selected = selectNonOverlappingReplacements(replacements);
|
|
65
|
+
const start = sourceOffset(node.startPosition, sourceLineStarts);
|
|
66
|
+
const end = sourceOffset(node.endPosition, sourceLineStarts);
|
|
67
|
+
if (start < 0 || end < start || end > source.length) return undefined;
|
|
68
|
+
|
|
69
|
+
let rendered = source.slice(start, end);
|
|
70
|
+
for (let index = selected.length - 1; index >= 0; index -= 1) {
|
|
71
|
+
const replacement = selected[index];
|
|
72
|
+
if (!replacement || replacement.start < start || replacement.end > end) continue;
|
|
73
|
+
const relativeStart = replacement.start - start;
|
|
74
|
+
const relativeEnd = replacement.end - start;
|
|
75
|
+
rendered = `${rendered.slice(0, relativeStart)}(…)${rendered.slice(relativeEnd)}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const displayName = normalizeCallName(rendered, grammarId, nodeType);
|
|
79
|
+
return displayName.length > 0 ? displayName : undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function collectReceiverArgumentLists(
|
|
83
|
+
node: SyntaxNodeLike,
|
|
84
|
+
replacements: TextReplacement[],
|
|
85
|
+
sourceLineStarts: readonly number[],
|
|
86
|
+
): void {
|
|
87
|
+
const argumentList = findArgumentList(node);
|
|
88
|
+
if (argumentList && hasNamedChildren(argumentList)) {
|
|
89
|
+
const start = sourceOffset(argumentList.startPosition, sourceLineStarts);
|
|
90
|
+
const end = sourceOffset(argumentList.endPosition, sourceLineStarts);
|
|
91
|
+
if (start >= 0 && end >= start) replacements.push({ start, end });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const receiver of receiverNodes(node)) {
|
|
95
|
+
collectReceiverArgumentLists(receiver, replacements, sourceLineStarts);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function findArgumentList(node: SyntaxNodeLike): SyntaxNodeLike | null {
|
|
100
|
+
if (!CALL_NODE_TYPES.has(node.type)) return null;
|
|
101
|
+
|
|
102
|
+
const field = node.childForFieldName("arguments");
|
|
103
|
+
if (field) return field;
|
|
104
|
+
|
|
105
|
+
const directArgumentList = node.children.find((child) => ARGUMENT_LIST_TYPES.has(child.type));
|
|
106
|
+
if (directArgumentList) return directArgumentList;
|
|
107
|
+
|
|
108
|
+
if (node.type !== "call_expression") return null;
|
|
109
|
+
const callSuffix = node.children.find((child) => child.type === "call_suffix");
|
|
110
|
+
return callSuffix?.children.find((child) => child.type === "value_arguments") ?? null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function receiverNodes(node: SyntaxNodeLike): SyntaxNodeLike[] {
|
|
114
|
+
for (const fieldName of RECEIVER_FIELDS) {
|
|
115
|
+
const field = node.childForFieldName(fieldName);
|
|
116
|
+
if (field) return [field];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (node.type === "call_expression") {
|
|
120
|
+
return namedChildren(node).filter(
|
|
121
|
+
(child) => child.type !== "call_suffix" && !ARGUMENT_LIST_TYPES.has(child.type),
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!RECEIVER_WRAPPER_TYPES.has(node.type)) return [];
|
|
126
|
+
return namedChildren(node).filter((child) => !ARGUMENT_LIST_TYPES.has(child.type));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function namedChildren(node: SyntaxNodeLike): CalleeSyntaxChild[] {
|
|
130
|
+
return (node as CalleeSyntaxNode).namedChildren ?? [];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function hasNamedChildren(node: SyntaxNodeLike): boolean {
|
|
134
|
+
return namedChildren(node).some((child) => !child.isExtra);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function selectNonOverlappingReplacements(
|
|
138
|
+
replacements: readonly TextReplacement[],
|
|
139
|
+
): TextReplacement[] {
|
|
140
|
+
const selected: TextReplacement[] = [];
|
|
141
|
+
const ordered = [...replacements].sort(
|
|
142
|
+
(left, right) => left.start - right.start || right.end - left.end,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
for (const replacement of ordered) {
|
|
146
|
+
const previous = selected[selected.length - 1];
|
|
147
|
+
if (previous && replacement.start < previous.end) continue;
|
|
148
|
+
selected.push(replacement);
|
|
149
|
+
}
|
|
150
|
+
return selected;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Build source offsets for Tree-sitter rows without decoding expression text. */
|
|
154
|
+
export function createSourceLineStarts(source: string): number[] {
|
|
155
|
+
const lineStarts = [0];
|
|
156
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
157
|
+
if (source[index] === "\n") lineStarts.push(index + 1);
|
|
158
|
+
}
|
|
159
|
+
return lineStarts;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function sourceOffset(point: SourcePoint, lineStarts: readonly number[]): number {
|
|
163
|
+
const lineStart = lineStarts[point.row];
|
|
164
|
+
return lineStart === undefined ? -1 : lineStart + point.column;
|
|
165
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
// biome-ignore-all lint/style/noExcessiveLinesPerFile: callee scope filtering and syntax display mapping stay together.
|
|
1
2
|
// Structural callee extraction — enclosing-scope lookup with per-language queries.
|
|
2
3
|
|
|
3
4
|
import type { CodeRequestControl } from "@mrclrchtr/supi-code-runtime/api";
|
|
4
5
|
import { validatePublicPositionBounds } from "../coordinates.ts";
|
|
5
6
|
import { detectGrammar } from "../language.ts";
|
|
7
|
+
import type { SyntaxNodeLike } from "../syntax-node.ts";
|
|
6
8
|
import type { GrammarId, SourceRange, TreeSitterResult } from "../types.ts";
|
|
7
9
|
import { queryParsedFile, type TreeSitterRuntime } from "../worker/runtime.ts";
|
|
8
10
|
import { normalizeCallName } from "./call-name.ts";
|
|
11
|
+
import { createCalleeDisplayName, createSourceLineStarts } from "./callee-display-name.ts";
|
|
9
12
|
import { extractScopeName } from "./scope.ts";
|
|
10
13
|
|
|
11
14
|
/** Result shape returned by lookupCalleesAt. */
|
|
@@ -16,6 +19,7 @@ export interface CalleesAtResult {
|
|
|
16
19
|
};
|
|
17
20
|
callees: Array<{
|
|
18
21
|
name: string;
|
|
22
|
+
displayName?: string;
|
|
19
23
|
range: SourceRange;
|
|
20
24
|
}>;
|
|
21
25
|
depth: "direct" | "deep";
|
|
@@ -138,7 +142,7 @@ function findEnclosingScope(
|
|
|
138
142
|
* 3. Runs a grammar-specific callee query.
|
|
139
143
|
* 4. Filters to captures within that enclosing scope.
|
|
140
144
|
* 5. Excludes nested function/method/callback scopes that do not contain the anchor.
|
|
141
|
-
* 6. Deduplicates
|
|
145
|
+
* 6. Deduplicates identical capture ranges and returns captures in source order.
|
|
142
146
|
*/
|
|
143
147
|
// biome-ignore lint/complexity/useMaxParams: provider function needs runtime + coordinates + depth
|
|
144
148
|
export async function lookupCalleesAt(
|
|
@@ -212,6 +216,7 @@ export async function lookupCalleesAt(
|
|
|
212
216
|
scopes,
|
|
213
217
|
tsPoint,
|
|
214
218
|
depth,
|
|
219
|
+
source,
|
|
215
220
|
);
|
|
216
221
|
|
|
217
222
|
const enclosingRange = nodeToSourceRange(enclosingNode);
|
|
@@ -318,7 +323,8 @@ function filterCalleeCaptures(
|
|
|
318
323
|
scopeTypes: ReadonlySet<string>,
|
|
319
324
|
anchor: { row: number; column: number },
|
|
320
325
|
depth: "direct" | "deep" = "direct",
|
|
321
|
-
|
|
326
|
+
source: string,
|
|
327
|
+
): Array<{ name: string; displayName?: string; range: SourceRange }> {
|
|
322
328
|
const excludeRanges: Array<{
|
|
323
329
|
startRow: number;
|
|
324
330
|
startColumn: number;
|
|
@@ -329,8 +335,11 @@ function filterCalleeCaptures(
|
|
|
329
335
|
collectInnerScopes(enclosingNode, scopeTypes, anchor, excludeRanges);
|
|
330
336
|
}
|
|
331
337
|
|
|
332
|
-
const
|
|
333
|
-
const callees: Array<{ name: string; range: SourceRange }> = [];
|
|
338
|
+
const seenRanges = new Set<string>();
|
|
339
|
+
const callees: Array<{ name: string; displayName?: string; range: SourceRange }> = [];
|
|
340
|
+
const sourceLineStarts = createSourceLineStarts(source);
|
|
341
|
+
const syntaxNodes = new Map<string, SyntaxNodeLike>();
|
|
342
|
+
indexSyntaxNodes(enclosingNode as NamedSyntaxNode, syntaxNodes);
|
|
334
343
|
|
|
335
344
|
const enclosingStartRow = enclosingNode.startPosition.row;
|
|
336
345
|
const enclosingEndRow = enclosingNode.endPosition.row;
|
|
@@ -366,17 +375,66 @@ function filterCalleeCaptures(
|
|
|
366
375
|
}
|
|
367
376
|
|
|
368
377
|
const name = normalizeCallName(capture.text, grammarId, capture.nodeType);
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
378
|
+
const rangeKey = sourceRangeKey(capture.range);
|
|
379
|
+
if (name.length === 0 || seenRanges.has(rangeKey)) continue;
|
|
380
|
+
seenRanges.add(rangeKey);
|
|
381
|
+
|
|
382
|
+
const syntaxNode = syntaxNodes.get(captureNodeKey(capture));
|
|
383
|
+
const displayName = syntaxNode
|
|
384
|
+
? createCalleeDisplayName({
|
|
385
|
+
node: syntaxNode,
|
|
386
|
+
grammarId,
|
|
387
|
+
nodeType: capture.nodeType,
|
|
388
|
+
source,
|
|
389
|
+
sourceLineStarts,
|
|
390
|
+
})
|
|
391
|
+
: undefined;
|
|
392
|
+
callees.push({
|
|
393
|
+
name,
|
|
394
|
+
...(displayName === undefined ? {} : { displayName }),
|
|
395
|
+
range: capture.range,
|
|
396
|
+
});
|
|
373
397
|
}
|
|
374
398
|
|
|
375
|
-
return callees;
|
|
399
|
+
return callees.sort((left, right) => compareSourceRanges(left.range, right.range));
|
|
376
400
|
}
|
|
377
401
|
|
|
378
402
|
// ── Internal helpers ──────────────────────────────────────────────────
|
|
379
403
|
|
|
404
|
+
function sourceRangeKey(range: SourceRange): string {
|
|
405
|
+
return `${range.startLine}:${range.startCharacter}:${range.endLine}:${range.endCharacter}`;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
type NamedSyntaxNode = SyntaxNodeLike & { namedChildren: NamedSyntaxNode[] };
|
|
409
|
+
|
|
410
|
+
/** Query captures use named nodes. Skip anonymous tokens and avoid recursive indexing. */
|
|
411
|
+
function indexSyntaxNodes(node: NamedSyntaxNode, index: Map<string, SyntaxNodeLike>): void {
|
|
412
|
+
const pending = [node];
|
|
413
|
+
while (pending.length > 0) {
|
|
414
|
+
const current = pending.pop();
|
|
415
|
+
if (!current) break;
|
|
416
|
+
index.set(syntaxNodeKey(current), current);
|
|
417
|
+
for (const child of current.namedChildren) pending.push(child);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function syntaxNodeKey(node: SyntaxNodeLike): string {
|
|
422
|
+
return `${node.type}:${node.startPosition.row}:${node.startPosition.column}:${node.endPosition.row}:${node.endPosition.column}`;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function captureNodeKey(capture: { nodeType: string; range: SourceRange }): string {
|
|
426
|
+
return `${capture.nodeType}:${capture.range.startLine - 1}:${capture.range.startCharacter - 1}:${capture.range.endLine - 1}:${capture.range.endCharacter - 1}`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function compareSourceRanges(left: SourceRange, right: SourceRange): number {
|
|
430
|
+
return (
|
|
431
|
+
left.startLine - right.startLine ||
|
|
432
|
+
left.startCharacter - right.startCharacter ||
|
|
433
|
+
left.endLine - right.endLine ||
|
|
434
|
+
left.endCharacter - right.endCharacter
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
380
438
|
/** Convert a tree-sitter node to a SourceRange using the source text for
|
|
381
439
|
* UTF-16 column conversion. */
|
|
382
440
|
function nodeToSourceRange(node: {
|
|
@@ -64,46 +64,47 @@ function plainValue(source, onError) {
|
|
|
64
64
|
}
|
|
65
65
|
if (badChar)
|
|
66
66
|
onError(0, 'BAD_SCALAR_START', `Plain value cannot start with ${badChar}`);
|
|
67
|
-
return
|
|
67
|
+
return unfoldLines(source);
|
|
68
68
|
}
|
|
69
69
|
function singleQuotedValue(source, onError) {
|
|
70
70
|
if (source[source.length - 1] !== "'" || source.length === 1)
|
|
71
71
|
onError(source.length, 'MISSING_CHAR', "Missing closing 'quote");
|
|
72
|
-
return
|
|
72
|
+
return unfoldLines(source.slice(1, -1)).replace(/''/g, "'");
|
|
73
73
|
}
|
|
74
|
-
function
|
|
74
|
+
function unfoldLines(source) {
|
|
75
|
+
const line = /(.*?)\r?\n/sy;
|
|
76
|
+
let match = line.exec(source);
|
|
77
|
+
if (!match)
|
|
78
|
+
return source;
|
|
75
79
|
/**
|
|
76
|
-
* The negative
|
|
80
|
+
* The negative lookbehinds in these RegExps are to
|
|
77
81
|
* prevent causing a polynomial search time in certain cases.
|
|
78
82
|
*
|
|
79
|
-
* The try-catch is for Safari
|
|
83
|
+
* The try-catch is for Safari < 16.4 and other old browsers:
|
|
80
84
|
* https://caniuse.com/js-regexp-lookbehind
|
|
81
85
|
*/
|
|
82
|
-
let
|
|
86
|
+
let trimEnd, trimBoth;
|
|
83
87
|
try {
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
trimEnd = new RegExp('(?<![ \t])[ \t]+$');
|
|
89
|
+
trimBoth = new RegExp('^[ \t]+|(?<![ \t])[ \t]+$', 'g');
|
|
86
90
|
}
|
|
87
91
|
catch {
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
trimEnd = /[ \t]+$/;
|
|
93
|
+
trimBoth = /^[ \t]+|[ \t]+$/g;
|
|
90
94
|
}
|
|
91
|
-
let
|
|
92
|
-
if (!match)
|
|
93
|
-
return source;
|
|
94
|
-
let res = match[1];
|
|
95
|
+
let res = match[1].replace(trimEnd, '');
|
|
95
96
|
let sep = ' ';
|
|
96
|
-
let pos =
|
|
97
|
-
line.lastIndex = pos;
|
|
97
|
+
let pos = line.lastIndex;
|
|
98
98
|
while ((match = line.exec(source))) {
|
|
99
|
-
|
|
99
|
+
const lm = match[1].replace(trimBoth, '');
|
|
100
|
+
if (lm === '') {
|
|
100
101
|
if (sep === '\n')
|
|
101
102
|
res += sep;
|
|
102
103
|
else
|
|
103
104
|
sep = '\n';
|
|
104
105
|
}
|
|
105
106
|
else {
|
|
106
|
-
res += sep +
|
|
107
|
+
res += sep + lm;
|
|
107
108
|
sep = ' ';
|
|
108
109
|
}
|
|
109
110
|
pos = line.lastIndex;
|
|
@@ -43,38 +43,40 @@ class Alias extends NodeBase {
|
|
|
43
43
|
if (node.anchor === this.source)
|
|
44
44
|
found = node;
|
|
45
45
|
}
|
|
46
|
+
if (found && ctx) {
|
|
47
|
+
const { anchors, doc, maxAliasCount } = ctx;
|
|
48
|
+
let data = anchors.get(found);
|
|
49
|
+
if (!data) {
|
|
50
|
+
// Resolve anchors for Node.prototype.toJS()
|
|
51
|
+
toJS(found, null, ctx);
|
|
52
|
+
data = anchors.get(found);
|
|
53
|
+
}
|
|
54
|
+
/* istanbul ignore if */
|
|
55
|
+
if (data?.res === undefined) {
|
|
56
|
+
const msg = 'This should not happen: Alias anchor was not resolved?';
|
|
57
|
+
throw new ReferenceError(msg);
|
|
58
|
+
}
|
|
59
|
+
if (maxAliasCount >= 0) {
|
|
60
|
+
data.count += 1;
|
|
61
|
+
if (data.aliasCount === 0)
|
|
62
|
+
data.aliasCount = getAliasCount(doc, found, anchors);
|
|
63
|
+
if (data.count * data.aliasCount > maxAliasCount) {
|
|
64
|
+
const msg = 'Excessive alias count indicates a resource exhaustion attack';
|
|
65
|
+
throw new ReferenceError(msg);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
46
69
|
return found;
|
|
47
70
|
}
|
|
48
71
|
toJSON(_arg, ctx) {
|
|
49
72
|
if (!ctx)
|
|
50
73
|
return { source: this.source };
|
|
51
|
-
const
|
|
52
|
-
const source = this.resolve(doc, ctx);
|
|
74
|
+
const source = this.resolve(ctx.doc, ctx);
|
|
53
75
|
if (!source) {
|
|
54
76
|
const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;
|
|
55
77
|
throw new ReferenceError(msg);
|
|
56
78
|
}
|
|
57
|
-
|
|
58
|
-
if (!data) {
|
|
59
|
-
// Resolve anchors for Node.prototype.toJS()
|
|
60
|
-
toJS(source, null, ctx);
|
|
61
|
-
data = anchors.get(source);
|
|
62
|
-
}
|
|
63
|
-
/* istanbul ignore if */
|
|
64
|
-
if (data?.res === undefined) {
|
|
65
|
-
const msg = 'This should not happen: Alias anchor was not resolved?';
|
|
66
|
-
throw new ReferenceError(msg);
|
|
67
|
-
}
|
|
68
|
-
if (maxAliasCount >= 0) {
|
|
69
|
-
data.count += 1;
|
|
70
|
-
if (data.aliasCount === 0)
|
|
71
|
-
data.aliasCount = getAliasCount(doc, source, anchors);
|
|
72
|
-
if (data.count * data.aliasCount > maxAliasCount) {
|
|
73
|
-
const msg = 'Excessive alias count indicates a resource exhaustion attack';
|
|
74
|
-
throw new ReferenceError(msg);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return data.res;
|
|
79
|
+
return ctx.anchors.get(source).res;
|
|
78
80
|
}
|
|
79
81
|
toString(ctx, _onComment, _onChompKeep) {
|
|
80
82
|
const src = `*${this.source}`;
|
|
@@ -66,46 +66,47 @@ function plainValue(source, onError) {
|
|
|
66
66
|
}
|
|
67
67
|
if (badChar)
|
|
68
68
|
onError(0, 'BAD_SCALAR_START', `Plain value cannot start with ${badChar}`);
|
|
69
|
-
return
|
|
69
|
+
return unfoldLines(source);
|
|
70
70
|
}
|
|
71
71
|
function singleQuotedValue(source, onError) {
|
|
72
72
|
if (source[source.length - 1] !== "'" || source.length === 1)
|
|
73
73
|
onError(source.length, 'MISSING_CHAR', "Missing closing 'quote");
|
|
74
|
-
return
|
|
74
|
+
return unfoldLines(source.slice(1, -1)).replace(/''/g, "'");
|
|
75
75
|
}
|
|
76
|
-
function
|
|
76
|
+
function unfoldLines(source) {
|
|
77
|
+
const line = /(.*?)\r?\n/sy;
|
|
78
|
+
let match = line.exec(source);
|
|
79
|
+
if (!match)
|
|
80
|
+
return source;
|
|
77
81
|
/**
|
|
78
|
-
* The negative
|
|
82
|
+
* The negative lookbehinds in these RegExps are to
|
|
79
83
|
* prevent causing a polynomial search time in certain cases.
|
|
80
84
|
*
|
|
81
|
-
* The try-catch is for Safari
|
|
85
|
+
* The try-catch is for Safari < 16.4 and other old browsers:
|
|
82
86
|
* https://caniuse.com/js-regexp-lookbehind
|
|
83
87
|
*/
|
|
84
|
-
let
|
|
88
|
+
let trimEnd, trimBoth;
|
|
85
89
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
trimEnd = new RegExp('(?<![ \t])[ \t]+$');
|
|
91
|
+
trimBoth = new RegExp('^[ \t]+|(?<![ \t])[ \t]+$', 'g');
|
|
88
92
|
}
|
|
89
93
|
catch {
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
trimEnd = /[ \t]+$/;
|
|
95
|
+
trimBoth = /^[ \t]+|[ \t]+$/g;
|
|
92
96
|
}
|
|
93
|
-
let
|
|
94
|
-
if (!match)
|
|
95
|
-
return source;
|
|
96
|
-
let res = match[1];
|
|
97
|
+
let res = match[1].replace(trimEnd, '');
|
|
97
98
|
let sep = ' ';
|
|
98
|
-
let pos =
|
|
99
|
-
line.lastIndex = pos;
|
|
99
|
+
let pos = line.lastIndex;
|
|
100
100
|
while ((match = line.exec(source))) {
|
|
101
|
-
|
|
101
|
+
const lm = match[1].replace(trimBoth, '');
|
|
102
|
+
if (lm === '') {
|
|
102
103
|
if (sep === '\n')
|
|
103
104
|
res += sep;
|
|
104
105
|
else
|
|
105
106
|
sep = '\n';
|
|
106
107
|
}
|
|
107
108
|
else {
|
|
108
|
-
res += sep +
|
|
109
|
+
res += sep + lm;
|
|
109
110
|
sep = ' ';
|
|
110
111
|
}
|
|
111
112
|
pos = line.lastIndex;
|
|
@@ -45,38 +45,40 @@ class Alias extends Node.NodeBase {
|
|
|
45
45
|
if (node.anchor === this.source)
|
|
46
46
|
found = node;
|
|
47
47
|
}
|
|
48
|
+
if (found && ctx) {
|
|
49
|
+
const { anchors, doc, maxAliasCount } = ctx;
|
|
50
|
+
let data = anchors.get(found);
|
|
51
|
+
if (!data) {
|
|
52
|
+
// Resolve anchors for Node.prototype.toJS()
|
|
53
|
+
toJS.toJS(found, null, ctx);
|
|
54
|
+
data = anchors.get(found);
|
|
55
|
+
}
|
|
56
|
+
/* istanbul ignore if */
|
|
57
|
+
if (data?.res === undefined) {
|
|
58
|
+
const msg = 'This should not happen: Alias anchor was not resolved?';
|
|
59
|
+
throw new ReferenceError(msg);
|
|
60
|
+
}
|
|
61
|
+
if (maxAliasCount >= 0) {
|
|
62
|
+
data.count += 1;
|
|
63
|
+
if (data.aliasCount === 0)
|
|
64
|
+
data.aliasCount = getAliasCount(doc, found, anchors);
|
|
65
|
+
if (data.count * data.aliasCount > maxAliasCount) {
|
|
66
|
+
const msg = 'Excessive alias count indicates a resource exhaustion attack';
|
|
67
|
+
throw new ReferenceError(msg);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
48
71
|
return found;
|
|
49
72
|
}
|
|
50
73
|
toJSON(_arg, ctx) {
|
|
51
74
|
if (!ctx)
|
|
52
75
|
return { source: this.source };
|
|
53
|
-
const
|
|
54
|
-
const source = this.resolve(doc, ctx);
|
|
76
|
+
const source = this.resolve(ctx.doc, ctx);
|
|
55
77
|
if (!source) {
|
|
56
78
|
const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;
|
|
57
79
|
throw new ReferenceError(msg);
|
|
58
80
|
}
|
|
59
|
-
|
|
60
|
-
if (!data) {
|
|
61
|
-
// Resolve anchors for Node.prototype.toJS()
|
|
62
|
-
toJS.toJS(source, null, ctx);
|
|
63
|
-
data = anchors.get(source);
|
|
64
|
-
}
|
|
65
|
-
/* istanbul ignore if */
|
|
66
|
-
if (data?.res === undefined) {
|
|
67
|
-
const msg = 'This should not happen: Alias anchor was not resolved?';
|
|
68
|
-
throw new ReferenceError(msg);
|
|
69
|
-
}
|
|
70
|
-
if (maxAliasCount >= 0) {
|
|
71
|
-
data.count += 1;
|
|
72
|
-
if (data.aliasCount === 0)
|
|
73
|
-
data.aliasCount = getAliasCount(doc, source, anchors);
|
|
74
|
-
if (data.count * data.aliasCount > maxAliasCount) {
|
|
75
|
-
const msg = 'Excessive alias count indicates a resource exhaustion attack';
|
|
76
|
-
throw new ReferenceError(msg);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return data.res;
|
|
81
|
+
return ctx.anchors.get(source).res;
|
|
80
82
|
}
|
|
81
83
|
toString(ctx, _onComment, _onChompKeep) {
|
|
82
84
|
const src = `*${this.source}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-code-intelligence",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "LSP and AST navigation, search, diagnostics, and refactoring",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"yaml": "^2.9.0",
|
|
38
|
-
"@mrclrchtr/supi-code-runtime": "
|
|
39
|
-
"@mrclrchtr/supi-
|
|
40
|
-
"@mrclrchtr/supi-
|
|
41
|
-
"@mrclrchtr/supi-
|
|
38
|
+
"@mrclrchtr/supi-code-runtime": "7.0.0",
|
|
39
|
+
"@mrclrchtr/supi-lsp": "7.0.0",
|
|
40
|
+
"@mrclrchtr/supi-core": "7.0.0",
|
|
41
|
+
"@mrclrchtr/supi-tree-sitter": "7.0.0"
|
|
42
42
|
},
|
|
43
43
|
"bundledDependencies": [
|
|
44
44
|
"@mrclrchtr/supi-code-runtime",
|
|
@@ -312,8 +312,7 @@ function evidenceIsComplete(
|
|
|
312
312
|
function diagnosticEvidenceReason(evidence: DiagnosticEvidenceSummary): string {
|
|
313
313
|
let reason = `Diagnostic evidence is partial: ${evidence.requested} requested, ${evidence.confirmed} confirmed, ${evidence.unconfirmed} unconfirmed, ${evidence.failed} failed, ${evidence.removed} removed.`;
|
|
314
314
|
if (evidence.unconfirmed > 0) {
|
|
315
|
-
reason +=
|
|
316
|
-
" Unconfirmed documents await a later diagnostic republish before their evidence can be confirmed (ADR 0021).";
|
|
315
|
+
reason += " Unconfirmed documents have no request-based confirmation.";
|
|
317
316
|
}
|
|
318
317
|
return reason;
|
|
319
318
|
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type DiagnosticEvidenceSummary,
|
|
17
17
|
type FileScopeDecision,
|
|
18
18
|
getFileScopeDecision,
|
|
19
|
+
isTsconfigApplicableFile,
|
|
19
20
|
TENTATIVE_PUSH_UNAVAILABLE_REASON,
|
|
20
21
|
type WorkspaceLspRuntime,
|
|
21
22
|
} from "@mrclrchtr/supi-lsp/api";
|
|
@@ -26,7 +27,7 @@ import type {
|
|
|
26
27
|
HealthDiagnosticScope,
|
|
27
28
|
} from "../../session/health-types.ts";
|
|
28
29
|
|
|
29
|
-
/** Collect a live per-file diagnostic pull with
|
|
30
|
+
/** Collect a live per-file diagnostic pull with an applicable config scope decision. */
|
|
30
31
|
export async function collectScopedFileDiagnostics(options: {
|
|
31
32
|
service: WorkspaceLspRuntime;
|
|
32
33
|
scope: Extract<HealthDiagnosticScope, { kind: "file" }>;
|
|
@@ -87,6 +88,8 @@ export async function collectScopedFileDiagnostics(options: {
|
|
|
87
88
|
* a failure to compute never fails the diagnostic request.
|
|
88
89
|
*/
|
|
89
90
|
export function fileScopeStatus(filePath: string, cwd: string): FileScopeDecision | null {
|
|
91
|
+
if (!isTsconfigApplicableFile(filePath)) return null;
|
|
92
|
+
|
|
90
93
|
try {
|
|
91
94
|
return getFileScopeDecision(filePath, cwd);
|
|
92
95
|
} catch {
|