@mrclrchtr/supi-tree-sitter 1.16.1 → 2.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 +18 -37
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +6 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +31 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/api.ts +8 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +10 -4
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config.ts +1 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/index.ts +6 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +9 -2
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +109 -4
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +5 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +3 -2
- package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +7 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +56 -2
- package/node_modules/@mrclrchtr/supi-code-runtime/src/evidence-badge.ts +40 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/index.ts +5 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +76 -3
- package/node_modules/@mrclrchtr/supi-code-runtime/src/workspace/runtime.ts +37 -2
- package/node_modules/@mrclrchtr/supi-core/package.json +6 -1
- package/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +31 -0
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +8 -0
- package/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +10 -4
- package/node_modules/@mrclrchtr/supi-core/src/config.ts +1 -0
- package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +6 -0
- package/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +9 -2
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +109 -4
- package/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +5 -1
- package/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
- package/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
- package/node_modules/web-tree-sitter/LICENSE +21 -0
- package/node_modules/web-tree-sitter/README.md +265 -0
- package/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs +4661 -0
- package/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs.map +7 -0
- package/node_modules/web-tree-sitter/debug/web-tree-sitter.js +4605 -0
- package/node_modules/web-tree-sitter/debug/web-tree-sitter.js.map +7 -0
- package/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
- package/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +57 -0
- package/node_modules/web-tree-sitter/package.json +100 -0
- package/node_modules/web-tree-sitter/web-tree-sitter.cjs +4063 -0
- package/node_modules/web-tree-sitter/web-tree-sitter.cjs.map +7 -0
- package/node_modules/web-tree-sitter/web-tree-sitter.d.cts +1025 -0
- package/node_modules/web-tree-sitter/web-tree-sitter.d.cts.map +58 -0
- package/node_modules/web-tree-sitter/web-tree-sitter.d.ts +1025 -0
- package/node_modules/web-tree-sitter/web-tree-sitter.d.ts.map +58 -0
- package/node_modules/web-tree-sitter/web-tree-sitter.js +4007 -0
- package/node_modules/web-tree-sitter/web-tree-sitter.js.map +7 -0
- package/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
- package/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +55 -0
- package/package.json +6 -12
- package/src/api.ts +29 -0
- package/src/index.ts +28 -0
- package/src/language.ts +5 -0
- package/src/provider/tree-sitter-provider.ts +14 -2
- package/src/session/runtime-controller.ts +157 -0
- package/src/session/session.ts +8 -1
- package/src/tool/call-sites.ts +86 -0
- package/src/tool/callees.ts +35 -16
- package/src/tool/structure.ts +1 -0
- package/src/types.ts +10 -0
- package/src/extension.ts +0 -1
- package/src/tool/formatting.ts +0 -104
- package/src/tool/guidance.ts +0 -35
- package/src/tool/handlers.ts +0 -196
- package/src/tool/register-tools.ts +0 -107
- package/src/tool/tool-specs.ts +0 -117
- package/src/tree-sitter.ts +0 -47
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-tree-sitter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "SuPi Tree-sitter extension — structural AST analysis for pi",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
"access": "public"
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|
|
19
|
-
"pi-package",
|
|
20
19
|
"pi",
|
|
21
20
|
"pi-coding-agent",
|
|
22
21
|
"pi-extension",
|
|
@@ -26,6 +25,7 @@
|
|
|
26
25
|
"code-navigation",
|
|
27
26
|
"structural-search"
|
|
28
27
|
],
|
|
28
|
+
"type": "module",
|
|
29
29
|
"files": [
|
|
30
30
|
"src/**/*.ts",
|
|
31
31
|
"src/**/*.json",
|
|
@@ -35,12 +35,13 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"web-tree-sitter": "^0.26.8",
|
|
38
|
-
"@mrclrchtr/supi-
|
|
39
|
-
"@mrclrchtr/supi-
|
|
38
|
+
"@mrclrchtr/supi-code-runtime": "2.0.0",
|
|
39
|
+
"@mrclrchtr/supi-core": "2.0.0"
|
|
40
40
|
},
|
|
41
41
|
"bundledDependencies": [
|
|
42
42
|
"@mrclrchtr/supi-code-runtime",
|
|
43
|
-
"@mrclrchtr/supi-core"
|
|
43
|
+
"@mrclrchtr/supi-core",
|
|
44
|
+
"web-tree-sitter"
|
|
44
45
|
],
|
|
45
46
|
"peerDependencies": {
|
|
46
47
|
"@earendil-works/pi-ai": "*",
|
|
@@ -58,16 +59,9 @@
|
|
|
58
59
|
"optional": true
|
|
59
60
|
}
|
|
60
61
|
},
|
|
61
|
-
"pi": {
|
|
62
|
-
"extensions": [
|
|
63
|
-
"./src/extension.ts"
|
|
64
|
-
],
|
|
65
|
-
"image": "https://raw.githubusercontent.com/mrclrchtr/supi/main/screenshots/supi-tree-sitter.png"
|
|
66
|
-
},
|
|
67
62
|
"main": "src/api.ts",
|
|
68
63
|
"exports": {
|
|
69
64
|
"./api": "./src/api.ts",
|
|
70
|
-
"./extension": "./src/extension.ts",
|
|
71
65
|
"./package.json": "./package.json",
|
|
72
66
|
"./provider/tree-sitter-provider": "./src/provider/tree-sitter-provider.ts"
|
|
73
67
|
},
|
package/src/api.ts
CHANGED
|
@@ -1,9 +1,38 @@
|
|
|
1
1
|
// Public tree-sitter session factory, shared session service access, and shared types.
|
|
2
|
+
//
|
|
3
|
+
// This package is library-only — no pi extension surface.
|
|
4
|
+
// Exports structured runtime/service APIs only.
|
|
5
|
+
// Tool handler string-formatting lives in @mrclrchtr/supi-code-intelligence.
|
|
2
6
|
|
|
7
|
+
// Language detection helpers
|
|
8
|
+
export {
|
|
9
|
+
detectGrammar,
|
|
10
|
+
getSupportedExtension,
|
|
11
|
+
getSupportedExtensions,
|
|
12
|
+
isJsTsGrammar,
|
|
13
|
+
isSupportedFile,
|
|
14
|
+
} from "./language.ts";
|
|
15
|
+
export { TreeSitterRuntime } from "./session/runtime.ts";
|
|
16
|
+
export type {
|
|
17
|
+
TsControllerState,
|
|
18
|
+
TsStartResult,
|
|
19
|
+
} from "./session/runtime-controller.ts";
|
|
20
|
+
export { TreeSitterRuntimeController } from "./session/runtime-controller.ts";
|
|
3
21
|
export { getSessionTreeSitterService } from "./session/service-registry.ts";
|
|
4
22
|
export { createTreeSitterSession } from "./session/session.ts";
|
|
23
|
+
|
|
24
|
+
// Structural extraction services (consumed by supi-code-intelligence tool execution)
|
|
25
|
+
export { extractCallSites } from "./tool/call-sites.ts";
|
|
26
|
+
export { lookupCalleesAt } from "./tool/callees.ts";
|
|
27
|
+
export { extractExports } from "./tool/exports.ts";
|
|
28
|
+
export { extractImports } from "./tool/imports.ts";
|
|
29
|
+
export { lookupNodeAt } from "./tool/node-at.ts";
|
|
30
|
+
export { collectOutline } from "./tool/outline.ts";
|
|
31
|
+
|
|
32
|
+
// Shared types
|
|
5
33
|
export type {
|
|
6
34
|
CalleesAtResult,
|
|
35
|
+
CallSiteMatch,
|
|
7
36
|
ExportRecord,
|
|
8
37
|
GrammarId,
|
|
9
38
|
ImportRecord,
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,37 @@
|
|
|
1
1
|
// Public session factory, shared session service access, and re-exports for @mrclrchtr/supi-tree-sitter.
|
|
2
|
+
//
|
|
3
|
+
// This package is library-only — no pi extension surface.
|
|
4
|
+
// Tool handler string-formatting lives in @mrclrchtr/supi-code-intelligence.
|
|
2
5
|
|
|
6
|
+
// Language detection helpers
|
|
7
|
+
export {
|
|
8
|
+
detectGrammar,
|
|
9
|
+
getSupportedExtension,
|
|
10
|
+
getSupportedExtensions,
|
|
11
|
+
isJsTsGrammar,
|
|
12
|
+
isSupportedFile,
|
|
13
|
+
} from "./language.ts";
|
|
14
|
+
// Provider implementation
|
|
15
|
+
export { createTreeSitterProvider } from "./provider/tree-sitter-provider.ts";
|
|
16
|
+
export { TreeSitterRuntime } from "./session/runtime.ts";
|
|
17
|
+
export type {
|
|
18
|
+
TsControllerState,
|
|
19
|
+
TsStartResult,
|
|
20
|
+
} from "./session/runtime-controller.ts";
|
|
21
|
+
export { TreeSitterRuntimeController } from "./session/runtime-controller.ts";
|
|
3
22
|
export { getSessionTreeSitterService } from "./session/service-registry.ts";
|
|
4
23
|
export { createTreeSitterSession } from "./session/session.ts";
|
|
24
|
+
// Structural extraction services (consumed by supi-code-intelligence tool execution)
|
|
25
|
+
export { extractCallSites } from "./tool/call-sites.ts";
|
|
26
|
+
export { lookupCalleesAt } from "./tool/callees.ts";
|
|
27
|
+
export { extractExports } from "./tool/exports.ts";
|
|
28
|
+
export { extractImports } from "./tool/imports.ts";
|
|
29
|
+
export { lookupNodeAt } from "./tool/node-at.ts";
|
|
30
|
+
export { collectOutline } from "./tool/outline.ts";
|
|
31
|
+
// Shared types
|
|
5
32
|
export type {
|
|
6
33
|
CalleesAtResult,
|
|
34
|
+
CallSiteMatch,
|
|
7
35
|
ExportRecord,
|
|
8
36
|
GrammarId,
|
|
9
37
|
ImportRecord,
|
package/src/language.ts
CHANGED
|
@@ -56,6 +56,11 @@ export function isSupportedFile(filePath: string): boolean {
|
|
|
56
56
|
return detectGrammar(filePath) !== undefined;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/** Return all supported file extensions (with leading dot). */
|
|
60
|
+
export function getSupportedExtensions(): string[] {
|
|
61
|
+
return [...SUPPORTED_EXTENSIONS];
|
|
62
|
+
}
|
|
63
|
+
|
|
59
64
|
/** Get the file extension if it's supported, otherwise undefined. */
|
|
60
65
|
export function getSupportedExtension(filePath: string): SupportedExtension | undefined {
|
|
61
66
|
const ext = path.extname(filePath).toLowerCase();
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import type {
|
|
5
5
|
CalleesData,
|
|
6
|
+
CallSite,
|
|
6
7
|
CodeResult,
|
|
7
8
|
ExportData,
|
|
8
9
|
ImportData,
|
|
@@ -12,6 +13,7 @@ import type {
|
|
|
12
13
|
} from "@mrclrchtr/supi-code-runtime/api";
|
|
13
14
|
import type {
|
|
14
15
|
CalleesAtResult,
|
|
16
|
+
CallSiteMatch,
|
|
15
17
|
ExportRecord,
|
|
16
18
|
ImportRecord,
|
|
17
19
|
NodeAtResult,
|
|
@@ -27,8 +29,8 @@ import type {
|
|
|
27
29
|
*/
|
|
28
30
|
export function createTreeSitterProvider(service: TreeSitterService): StructuralProvider {
|
|
29
31
|
return {
|
|
30
|
-
async calleesAt(file, line, character) {
|
|
31
|
-
const result = await service.calleesAt(file, line, character);
|
|
32
|
+
async calleesAt(file, line, character, depth?) {
|
|
33
|
+
const result = await service.calleesAt(file, line, character, depth);
|
|
32
34
|
return mapTreeSitterResult(result, mapCalleesAtResult);
|
|
33
35
|
},
|
|
34
36
|
|
|
@@ -51,6 +53,11 @@ export function createTreeSitterProvider(service: TreeSitterService): Structural
|
|
|
51
53
|
const result = await service.nodeAt(file, line, character);
|
|
52
54
|
return mapTreeSitterResult(result, mapNodeAtResult);
|
|
53
55
|
},
|
|
56
|
+
|
|
57
|
+
async callSites(file) {
|
|
58
|
+
const result = await service.callSites(file);
|
|
59
|
+
return mapTreeSitterResult(result, mapCallSites);
|
|
60
|
+
},
|
|
54
61
|
};
|
|
55
62
|
}
|
|
56
63
|
|
|
@@ -140,5 +147,10 @@ function mapCalleesAtResult(result: CalleesAtResult): CalleesData {
|
|
|
140
147
|
name: c.name,
|
|
141
148
|
startLine: c.range.startLine,
|
|
142
149
|
})),
|
|
150
|
+
depth: result.depth,
|
|
143
151
|
};
|
|
144
152
|
}
|
|
153
|
+
|
|
154
|
+
function mapCallSites(matches: CallSiteMatch[]): CallSite[] {
|
|
155
|
+
return matches.map((m) => ({ name: m.name, startLine: m.startLine }));
|
|
156
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// Tree-sitter session runtime controller — pi-independent lifecycle for structural analysis.
|
|
2
|
+
//
|
|
3
|
+
// This controller owns runtime creation/disposal for one cwd:
|
|
4
|
+
// - Creates and disposes the TreeSitterRuntime
|
|
5
|
+
// - Creates TreeSitterService from the runtime
|
|
6
|
+
// - Publishes SessionTreeSitterService state through the existing registry
|
|
7
|
+
// - Exposes ready/unavailable lifecycle for umbrella adapter consumption
|
|
8
|
+
|
|
9
|
+
import type { WorkspaceRuntime } from "@mrclrchtr/supi-code-runtime/api";
|
|
10
|
+
import type { SessionTreeSitterService, TreeSitterService } from "../types.ts";
|
|
11
|
+
import { TreeSitterRuntime } from "./runtime.ts";
|
|
12
|
+
import {
|
|
13
|
+
registerTreeSitterCapabilities,
|
|
14
|
+
unregisterTreeSitterCapabilities,
|
|
15
|
+
} from "./runtime-registration.ts";
|
|
16
|
+
import { clearSessionTreeSitterService, setSessionTreeSitterService } from "./service-registry.ts";
|
|
17
|
+
import { createTreeSitterService } from "./session.ts";
|
|
18
|
+
|
|
19
|
+
// ── Types ─────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
/** Starting state before or after shutdown. */
|
|
22
|
+
export type TsControllerState = TsControllerInitial | TsControllerReady | TsControllerUnavailable;
|
|
23
|
+
|
|
24
|
+
interface TsControllerInitial {
|
|
25
|
+
kind: "initial";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface TsControllerReady {
|
|
29
|
+
kind: "ready";
|
|
30
|
+
runtime: TreeSitterRuntime;
|
|
31
|
+
service: TreeSitterService;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface TsControllerUnavailable {
|
|
35
|
+
kind: "unavailable";
|
|
36
|
+
reason: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Result type from {@link TreeSitterRuntimeController.start}. */
|
|
40
|
+
export type TsStartResult = { kind: "ready" } | { kind: "unavailable"; reason: string };
|
|
41
|
+
|
|
42
|
+
// ── Controller ────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Pi-independent Tree-sitter session lifecycle controller.
|
|
46
|
+
*
|
|
47
|
+
* Use this in the umbrella extension (supi-code-intelligence) instead of
|
|
48
|
+
* reaching into substrate extension internals.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const controller = new TreeSitterRuntimeController(cwd);
|
|
53
|
+
* const result = await controller.start();
|
|
54
|
+
* if (result.kind === "ready") {
|
|
55
|
+
* // use controller.service, controller.runtime
|
|
56
|
+
* }
|
|
57
|
+
* // later
|
|
58
|
+
* await controller.shutdown();
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export class TreeSitterRuntimeController {
|
|
62
|
+
readonly #cwd: string;
|
|
63
|
+
#state: TsControllerState;
|
|
64
|
+
#runtime: WorkspaceRuntime | null;
|
|
65
|
+
|
|
66
|
+
constructor(cwd: string, runtime?: WorkspaceRuntime) {
|
|
67
|
+
this.#cwd = cwd;
|
|
68
|
+
this.#state = { kind: "initial" };
|
|
69
|
+
this.#runtime = runtime ?? null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The workspace cwd this controller was created for. */
|
|
73
|
+
get cwd(): string {
|
|
74
|
+
return this.#cwd;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Current controller state. */
|
|
78
|
+
get kind(): TsControllerState["kind"] {
|
|
79
|
+
return this.#state.kind;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The TreeSitterService (structural operations), available when state is "ready". */
|
|
83
|
+
get service(): SessionTreeSitterService | null {
|
|
84
|
+
return this.#state.kind === "ready" ? this.#state.service : null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The TreeSitterRuntime, available when state is "ready". */
|
|
88
|
+
get runtime(): TreeSitterRuntime | null {
|
|
89
|
+
return this.#state.kind === "ready" ? this.#state.runtime : null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Attach a WorkspaceRuntime for capability registration. */
|
|
93
|
+
setRuntime(runtime: WorkspaceRuntime): void {
|
|
94
|
+
this.#runtime = runtime;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Start the Tree-sitter session.
|
|
99
|
+
*
|
|
100
|
+
* Creates the runtime, wires the service, and publishes state
|
|
101
|
+
* into the shared registries. Validates that WASM initialization
|
|
102
|
+
* succeeds, returning `unavailable` if it fails.
|
|
103
|
+
*/
|
|
104
|
+
async start(): Promise<TsStartResult> {
|
|
105
|
+
// Restart safety: dispose any existing runtime
|
|
106
|
+
if (this.#state.kind === "ready") {
|
|
107
|
+
this.#state.runtime.dispose();
|
|
108
|
+
if (this.#runtime) unregisterTreeSitterCapabilities(this.#runtime, this.#cwd);
|
|
109
|
+
clearSessionTreeSitterService(this.#cwd);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.#state = { kind: "unavailable", reason: "Initializing Tree-sitter" };
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
const treeSitterRuntime = new TreeSitterRuntime(this.#cwd);
|
|
116
|
+
|
|
117
|
+
// Probe WASM initialization by loading the JavaScript grammar
|
|
118
|
+
// (always vendored). This catches missing web-tree-sitter or
|
|
119
|
+
// corrupted WASM early rather than deferring to first tool use.
|
|
120
|
+
await treeSitterRuntime.ensureGrammarParser("javascript");
|
|
121
|
+
|
|
122
|
+
const service = createTreeSitterService(treeSitterRuntime);
|
|
123
|
+
setSessionTreeSitterService(this.#cwd, service);
|
|
124
|
+
|
|
125
|
+
if (this.#runtime) {
|
|
126
|
+
registerTreeSitterCapabilities(this.#runtime, this.#cwd, service);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
this.#state = { kind: "ready", runtime: treeSitterRuntime, service };
|
|
130
|
+
return { kind: "ready" };
|
|
131
|
+
} catch (error: unknown) {
|
|
132
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
133
|
+
this.#state = { kind: "unavailable", reason };
|
|
134
|
+
clearSessionTreeSitterService(this.#cwd);
|
|
135
|
+
return { kind: "unavailable", reason };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Shut down the Tree-sitter session.
|
|
141
|
+
*
|
|
142
|
+
* Disposes the runtime and clears all published state.
|
|
143
|
+
*/
|
|
144
|
+
async shutdown(): Promise<void> {
|
|
145
|
+
if (this.#runtime && this.#cwd) {
|
|
146
|
+
unregisterTreeSitterCapabilities(this.#runtime, this.#cwd);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
clearSessionTreeSitterService(this.#cwd);
|
|
150
|
+
|
|
151
|
+
if (this.#state.kind === "ready") {
|
|
152
|
+
this.#state.runtime.dispose();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
this.#state = { kind: "initial" };
|
|
156
|
+
}
|
|
157
|
+
}
|
package/src/session/session.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { detectGrammar, isJsTsGrammar } from "../language.ts";
|
|
4
4
|
import {
|
|
5
|
+
extractCallSites,
|
|
5
6
|
extractExports,
|
|
6
7
|
extractImports,
|
|
7
8
|
extractOutline,
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
} from "../tool/structure.ts";
|
|
11
12
|
import type {
|
|
12
13
|
CalleesAtResult,
|
|
14
|
+
CallSiteMatch,
|
|
13
15
|
ExportRecord,
|
|
14
16
|
ImportRecord,
|
|
15
17
|
NodeAtResult,
|
|
@@ -97,8 +99,13 @@ export function createTreeSitterService(runtime: TreeSitterRuntime): TreeSitterS
|
|
|
97
99
|
file: string,
|
|
98
100
|
line: number,
|
|
99
101
|
character: number,
|
|
102
|
+
depth?: "direct" | "deep",
|
|
100
103
|
): Promise<TreeSitterResult<CalleesAtResult>> {
|
|
101
|
-
return lookupCalleesAt(runtime, file, line, character);
|
|
104
|
+
return lookupCalleesAt(runtime, file, line, character, depth);
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
async callSites(file: string): Promise<TreeSitterResult<CallSiteMatch[]>> {
|
|
108
|
+
return extractCallSites(runtime, file);
|
|
102
109
|
},
|
|
103
110
|
};
|
|
104
111
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/** File-wide call-site extraction using per-grammar tree-sitter queries. */
|
|
2
|
+
|
|
3
|
+
import { detectGrammar } from "../language.ts";
|
|
4
|
+
import type { TreeSitterRuntime } from "../session/runtime.ts";
|
|
5
|
+
import type { CallSiteMatch, GrammarId, TreeSitterResult } from "../types.ts";
|
|
6
|
+
|
|
7
|
+
// ── Per-grammar call-site queries ─────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
// JS/TS/TSX queries that capture the full callee expression, not just the leaf name.
|
|
10
|
+
// `(_)` matches any expression node, giving us the full callee including member chains
|
|
11
|
+
// and nested call expressions (e.g. `factory()()` captures `factory()` for the outer call).
|
|
12
|
+
const JS_TS_CALL_SITE_QUERY = [
|
|
13
|
+
// Regular calls: capture the whole callee expression
|
|
14
|
+
"(call_expression function: (_) @call)",
|
|
15
|
+
// New expressions: capture the constructor expression
|
|
16
|
+
"(new_expression constructor: (_) @call)",
|
|
17
|
+
].join("\n");
|
|
18
|
+
|
|
19
|
+
const CALL_SITE_QUERIES: Partial<Record<GrammarId, string>> = {
|
|
20
|
+
javascript: JS_TS_CALL_SITE_QUERY,
|
|
21
|
+
typescript: JS_TS_CALL_SITE_QUERY,
|
|
22
|
+
tsx: JS_TS_CALL_SITE_QUERY,
|
|
23
|
+
python:
|
|
24
|
+
"(call function: (identifier) @call) (call function: (attribute attribute: (identifier) @call))",
|
|
25
|
+
rust: "(call_expression function: (identifier) @call) (call_expression function: (field_expression field: (field_identifier) @call)) (macro_invocation macro: (identifier) @call)",
|
|
26
|
+
go: "(call_expression function: (identifier) @call) (call_expression function: (selector_expression field: (field_identifier) @call))",
|
|
27
|
+
c: "(call_expression function: (identifier) @call)",
|
|
28
|
+
cpp: "(call_expression function: (identifier) @call) (call_expression function: (field_expression field: (field_identifier) @call))",
|
|
29
|
+
java: "(method_invocation name: (identifier) @call) (object_creation_expression type: (type_identifier) @call)",
|
|
30
|
+
kotlin:
|
|
31
|
+
"(call_expression . (simple_identifier) @call) (call_expression . (navigation_expression . (simple_identifier) @call))",
|
|
32
|
+
ruby: "(call method: (identifier) @call)",
|
|
33
|
+
bash: "(command name: (word) @call)",
|
|
34
|
+
r: "(call function: (identifier) @call)",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Extract all call-site callee expressions from a file.
|
|
39
|
+
*
|
|
40
|
+
* Returns a deduplicated list of { name, startLine } for every call-site
|
|
41
|
+
* captured by the grammar-specific query.
|
|
42
|
+
*/
|
|
43
|
+
export async function extractCallSites(
|
|
44
|
+
runtime: TreeSitterRuntime,
|
|
45
|
+
filePath: string,
|
|
46
|
+
): Promise<TreeSitterResult<CallSiteMatch[]>> {
|
|
47
|
+
const grammarId = detectGrammar(filePath);
|
|
48
|
+
if (!grammarId) {
|
|
49
|
+
return {
|
|
50
|
+
kind: "unsupported-language",
|
|
51
|
+
file: filePath,
|
|
52
|
+
message: `Unsupported file: ${filePath}`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const queryStr = CALL_SITE_QUERIES[grammarId];
|
|
57
|
+
if (!queryStr) {
|
|
58
|
+
return {
|
|
59
|
+
kind: "unsupported-language",
|
|
60
|
+
file: filePath,
|
|
61
|
+
message: `callSites is not supported for ${grammarId} files`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const queryResult = await runtime.queryFile(filePath, queryStr);
|
|
66
|
+
if (queryResult.kind !== "success") return queryResult;
|
|
67
|
+
|
|
68
|
+
const captures = queryResult.data;
|
|
69
|
+
const seen = new Set<string>();
|
|
70
|
+
const matches: CallSiteMatch[] = [];
|
|
71
|
+
|
|
72
|
+
for (const capture of captures) {
|
|
73
|
+
// Normalize: trim whitespace and collapse internal whitespace to single space
|
|
74
|
+
const name = capture.text.trim().replace(/\s+/g, " ");
|
|
75
|
+
if (name.length === 0) continue;
|
|
76
|
+
|
|
77
|
+
// Deduplicate by name + line
|
|
78
|
+
const key = `${name}:${capture.range.startLine}`;
|
|
79
|
+
if (seen.has(key)) continue;
|
|
80
|
+
seen.add(key);
|
|
81
|
+
|
|
82
|
+
matches.push({ name, startLine: capture.range.startLine });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { kind: "success", data: matches };
|
|
86
|
+
}
|
package/src/tool/callees.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface CalleesAtResult {
|
|
|
14
14
|
name: string;
|
|
15
15
|
range: SourceRange;
|
|
16
16
|
}>;
|
|
17
|
+
depth: "direct" | "deep";
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
// ── Per-grammar callee queries ────────────────────────────────────────
|
|
@@ -103,13 +104,14 @@ function extractScopeName(_type: string, text: string): string {
|
|
|
103
104
|
// ── Main entrypoint ──────────────────────────────────────────────────
|
|
104
105
|
|
|
105
106
|
/**
|
|
106
|
-
* Extract structural callee calls for a file at the given position.
|
|
107
|
+
* Extract direct structural callee calls for a file at the given position.
|
|
107
108
|
*
|
|
108
109
|
* 1. Parses the file with the Tree-sitter runtime.
|
|
109
|
-
* 2. Finds the enclosing function/method scope at the position.
|
|
110
|
+
* 2. Finds the enclosing function/method/callback scope at the position.
|
|
110
111
|
* 3. Runs a grammar-specific callee query.
|
|
111
|
-
* 4. Filters to captures within
|
|
112
|
-
* 5.
|
|
112
|
+
* 4. Filters to captures within that enclosing scope.
|
|
113
|
+
* 5. Excludes nested function/method/callback scopes that do not contain the anchor.
|
|
114
|
+
* 6. Deduplicates by name.
|
|
113
115
|
*/
|
|
114
116
|
/** Validate that coordinates are usable and grammar is supported. */
|
|
115
117
|
function validateCalleeInput(
|
|
@@ -165,11 +167,13 @@ function findEnclosingScope(
|
|
|
165
167
|
return null;
|
|
166
168
|
}
|
|
167
169
|
|
|
170
|
+
// biome-ignore lint/complexity/useMaxParams: provider function needs runtime + coordinates + depth
|
|
168
171
|
export async function lookupCalleesAt(
|
|
169
172
|
runtime: TreeSitterRuntime,
|
|
170
173
|
filePath: string,
|
|
171
174
|
line: number,
|
|
172
175
|
character: number,
|
|
176
|
+
depth: "direct" | "deep" = "direct",
|
|
173
177
|
): Promise<TreeSitterResult<CalleesAtResult>> {
|
|
174
178
|
// Validate coordinates and grammar
|
|
175
179
|
const validation = validateCalleeInput(filePath, line, character);
|
|
@@ -218,7 +222,13 @@ export async function lookupCalleesAt(
|
|
|
218
222
|
};
|
|
219
223
|
}
|
|
220
224
|
|
|
221
|
-
const callees = filterCalleeCaptures(
|
|
225
|
+
const callees = filterCalleeCaptures(
|
|
226
|
+
queryResult.data,
|
|
227
|
+
enclosingNode,
|
|
228
|
+
scopes,
|
|
229
|
+
tsPoint.row,
|
|
230
|
+
depth,
|
|
231
|
+
);
|
|
222
232
|
|
|
223
233
|
const enclosingRange = nodeToSourceRange(enclosingNode);
|
|
224
234
|
const scopeName = extractScopeName(enclosingNode.type, enclosingNode.text);
|
|
@@ -231,6 +241,7 @@ export async function lookupCalleesAt(
|
|
|
231
241
|
range: enclosingRange,
|
|
232
242
|
},
|
|
233
243
|
callees,
|
|
244
|
+
depth,
|
|
234
245
|
},
|
|
235
246
|
};
|
|
236
247
|
} finally {
|
|
@@ -239,9 +250,9 @@ export async function lookupCalleesAt(
|
|
|
239
250
|
}
|
|
240
251
|
|
|
241
252
|
/**
|
|
242
|
-
* Recursively collect line ranges of inner function/method scopes that
|
|
243
|
-
* contain the anchor row. Captures from these ranges are excluded so
|
|
244
|
-
* nested
|
|
253
|
+
* Recursively collect line ranges of inner function/method/callback scopes that
|
|
254
|
+
* do not contain the anchor row. Captures from these ranges are excluded so
|
|
255
|
+
* nested calls are not attributed to the parent scope.
|
|
245
256
|
*/
|
|
246
257
|
function collectInnerScopes(
|
|
247
258
|
node: {
|
|
@@ -281,18 +292,24 @@ function collectInnerScopes(
|
|
|
281
292
|
}
|
|
282
293
|
|
|
283
294
|
/**
|
|
284
|
-
* Filter query captures to only those within the enclosing scope
|
|
285
|
-
*
|
|
295
|
+
* Filter query captures to only those within the enclosing scope.
|
|
296
|
+
* In `direct` depth, excludes captures that fall within inner nested
|
|
297
|
+
* function/callback scopes. In `deep` depth, all captures within the
|
|
298
|
+
* enclosing scope are included regardless of nesting.
|
|
286
299
|
*/
|
|
300
|
+
// biome-ignore lint/complexity/useMaxParams: filtering needs captures + node context + depth
|
|
287
301
|
function filterCalleeCaptures(
|
|
288
302
|
captures: Array<{ range: SourceRange; text: string }>,
|
|
289
303
|
// biome-ignore lint/suspicious/noExplicitAny: tree-sitter SyntaxNode is complex
|
|
290
304
|
enclosingNode: any,
|
|
291
305
|
scopeTypes: ReadonlySet<string>,
|
|
292
306
|
anchorRow: number,
|
|
307
|
+
depth: "direct" | "deep" = "direct",
|
|
293
308
|
): Array<{ name: string; range: SourceRange }> {
|
|
294
309
|
const excludeRanges: Array<{ startRow: number; endRow: number }> = [];
|
|
295
|
-
|
|
310
|
+
if (depth === "direct") {
|
|
311
|
+
collectInnerScopes(enclosingNode, scopeTypes, anchorRow, excludeRanges);
|
|
312
|
+
}
|
|
296
313
|
|
|
297
314
|
const seen = new Set<string>();
|
|
298
315
|
const callees: Array<{ name: string; range: SourceRange }> = [];
|
|
@@ -306,11 +323,13 @@ function filterCalleeCaptures(
|
|
|
306
323
|
continue;
|
|
307
324
|
}
|
|
308
325
|
|
|
309
|
-
//
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
326
|
+
// In direct depth, exclude captures that fall within inner nested function scopes
|
|
327
|
+
if (depth === "direct") {
|
|
328
|
+
const isInInner = excludeRanges.some(
|
|
329
|
+
(exc) => capture.range.startLine >= exc.startRow && capture.range.endLine <= exc.endRow,
|
|
330
|
+
);
|
|
331
|
+
if (isInInner) continue;
|
|
332
|
+
}
|
|
314
333
|
|
|
315
334
|
const name = capture.text.replace(/\s+/g, "").slice(0, 60);
|
|
316
335
|
if (name.length === 0 || seen.has(name)) continue;
|
package/src/tool/structure.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -56,6 +56,7 @@ export interface CalleesAtResult {
|
|
|
56
56
|
name: string;
|
|
57
57
|
range: SourceRange;
|
|
58
58
|
}>;
|
|
59
|
+
depth: "direct" | "deep";
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
/** Query capture result. */
|
|
@@ -66,6 +67,12 @@ export interface QueryCapture {
|
|
|
66
67
|
text: string;
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
/** A single call-site match from tree-sitter analysis. */
|
|
71
|
+
export interface CallSiteMatch {
|
|
72
|
+
name: string;
|
|
73
|
+
startLine: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
69
76
|
/** Shared Tree-sitter service surface, independent of lifecycle ownership. */
|
|
70
77
|
export interface TreeSitterService {
|
|
71
78
|
/** Validate that a supported file can be read and parsed; does not expose the raw tree. */
|
|
@@ -85,7 +92,10 @@ export interface TreeSitterService {
|
|
|
85
92
|
file: string,
|
|
86
93
|
line: number,
|
|
87
94
|
character: number,
|
|
95
|
+
depth?: "direct" | "deep",
|
|
88
96
|
): Promise<TreeSitterResult<CalleesAtResult>>;
|
|
97
|
+
/** Extract all call-site identifiers in a file. */
|
|
98
|
+
callSites(file: string): Promise<TreeSitterResult<CallSiteMatch[]>>;
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
/** Owned Tree-sitter session that must release its runtime resources. */
|
package/src/extension.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "./tree-sitter.ts";
|