@mrclrchtr/supi-tree-sitter 10.0.0 → 11.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 +63 -17
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/picomatch/LICENSE +21 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/picomatch/README.md +743 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/picomatch/index.js +17 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/picomatch/lib/constants.js +184 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/picomatch/lib/parse.js +1425 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/picomatch/lib/picomatch.js +361 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/picomatch/lib/scan.js +403 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/picomatch/lib/utils.js +72 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/picomatch/package.json +83 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/picomatch/posix.js +3 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +4 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +11 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/file-associations.ts +190 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +22 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/package.json +3 -3
- package/src/language.ts +16 -10
- package/src/operation-support.ts +13 -6
- package/src/provider/tree-sitter-provider.ts +19 -1
- package/src/session/runtime-controller.ts +18 -4
- package/src/session/session.ts +10 -3
- package/src/session/structural-worker-client.ts +17 -2
- package/src/session/structural-worker-process.ts +22 -5
- package/src/session/structural-worker-protocol.ts +36 -1
- package/src/syntax-errors.ts +73 -0
- package/src/syntax-node.ts +4 -0
- package/src/tool/call-sites.ts +15 -9
- package/src/tool/callees.ts +24 -15
- package/src/tool/exports.ts +8 -1
- package/src/tool/imports.ts +233 -7
- package/src/tool/node-at.ts +7 -3
- package/src/tool/outline-expansion.ts +78 -0
- package/src/tool/outline-jvm.ts +25 -4
- package/src/tool/outline-nested.ts +390 -0
- package/src/tool/outline.ts +49 -28
- package/src/types.ts +16 -0
- package/src/worker/process-host.mjs +61 -24
- package/src/worker/runtime.ts +44 -10
- package/src/worker/service.ts +13 -9
- package/src/worker/worker-main.ts +15 -2
|
@@ -11,44 +11,63 @@ if (
|
|
|
11
11
|
throw new Error("Structural Worker process received invalid startup data");
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
const worker = new Worker(new URL("./bootstrap.mjs", import.meta.url), {
|
|
15
|
-
execArgv: [],
|
|
16
|
-
workerData: { cwd, generation },
|
|
17
|
-
});
|
|
18
14
|
const cancellationFlags = new Map();
|
|
15
|
+
let worker = null;
|
|
19
16
|
let closing = false;
|
|
20
17
|
let workerExited = false;
|
|
21
18
|
|
|
22
|
-
worker.on("message", (message) => {
|
|
23
|
-
if (message.kind === "terminal") cancellationFlags.delete(message.requestId);
|
|
24
|
-
send({ channel: "supi-tree-sitter-worker", message });
|
|
25
|
-
});
|
|
26
|
-
worker.on("error", (error) => {
|
|
27
|
-
send(
|
|
28
|
-
{
|
|
29
|
-
channel: "supi-tree-sitter-worker",
|
|
30
|
-
message: { kind: "worker-error", message: error.message },
|
|
31
|
-
},
|
|
32
|
-
() => stopWorker(1),
|
|
33
|
-
);
|
|
34
|
-
});
|
|
35
|
-
worker.on("exit", (code) => {
|
|
36
|
-
workerExited = true;
|
|
37
|
-
cancellationFlags.clear();
|
|
38
|
-
process.exitCode = code === 0 ? 1 : code;
|
|
39
|
-
disconnect();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
19
|
process.on("message", (value) => {
|
|
43
20
|
if (!isParentChannel(value)) {
|
|
44
21
|
stopWorker(1);
|
|
45
22
|
return;
|
|
46
23
|
}
|
|
24
|
+
if (!worker) {
|
|
25
|
+
if (!isInitialization(value.message)) {
|
|
26
|
+
stopWorker(1);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
startWorker(value.message);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (value.message.kind === "initialize") {
|
|
33
|
+
stopWorker(1);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
47
36
|
forwardToWorker(value.message);
|
|
48
37
|
});
|
|
49
38
|
process.on("disconnect", () => stopWorker());
|
|
50
39
|
if (!process.connected) stopWorker();
|
|
51
40
|
|
|
41
|
+
function startWorker(initialization) {
|
|
42
|
+
worker = new Worker(new URL("./bootstrap.mjs", import.meta.url), {
|
|
43
|
+
execArgv: [],
|
|
44
|
+
workerData: {
|
|
45
|
+
cwd,
|
|
46
|
+
generation,
|
|
47
|
+
fileAssociations: initialization.fileAssociations,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
worker.on("message", (message) => {
|
|
51
|
+
if (message.kind === "terminal") cancellationFlags.delete(message.requestId);
|
|
52
|
+
send({ channel: "supi-tree-sitter-worker", message });
|
|
53
|
+
});
|
|
54
|
+
worker.on("error", (error) => {
|
|
55
|
+
send(
|
|
56
|
+
{
|
|
57
|
+
channel: "supi-tree-sitter-worker",
|
|
58
|
+
message: { kind: "worker-error", message: error.message },
|
|
59
|
+
},
|
|
60
|
+
() => stopWorker(1),
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
worker.on("exit", (code) => {
|
|
64
|
+
workerExited = true;
|
|
65
|
+
cancellationFlags.clear();
|
|
66
|
+
process.exitCode = code === 0 ? 1 : code;
|
|
67
|
+
disconnect();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
52
71
|
function forwardToWorker(message) {
|
|
53
72
|
if (!message || typeof message !== "object") {
|
|
54
73
|
throw new Error("Structural Worker process received an invalid message");
|
|
@@ -70,6 +89,20 @@ function forwardToWorker(message) {
|
|
|
70
89
|
worker.postMessage(message);
|
|
71
90
|
}
|
|
72
91
|
|
|
92
|
+
function isInitialization(value) {
|
|
93
|
+
return (
|
|
94
|
+
value !== null &&
|
|
95
|
+
typeof value === "object" &&
|
|
96
|
+
!Array.isArray(value) &&
|
|
97
|
+
Object.keys(value).length === 5 &&
|
|
98
|
+
value.kind === "initialize" &&
|
|
99
|
+
value.version === 1 &&
|
|
100
|
+
value.generation === generation &&
|
|
101
|
+
value.cwd === cwd &&
|
|
102
|
+
Array.isArray(value.fileAssociations)
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
73
106
|
function isParentChannel(value) {
|
|
74
107
|
return (
|
|
75
108
|
value !== null &&
|
|
@@ -105,5 +138,9 @@ function stopWorker(exitCode) {
|
|
|
105
138
|
closing = true;
|
|
106
139
|
if (exitCode !== undefined) process.exitCode = exitCode;
|
|
107
140
|
cancellationFlags.clear();
|
|
141
|
+
if (!worker) {
|
|
142
|
+
disconnect();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
108
145
|
void worker.terminate().finally(disconnect);
|
|
109
146
|
}
|
package/src/worker/runtime.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Tree-sitter runtime — parser management, parse, and query services.
|
|
3
3
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
+
import type { CFamilyFileAssociationResolver } from "@mrclrchtr/supi-code-runtime/api";
|
|
5
6
|
import { resolveToolPath } from "@mrclrchtr/supi-core/path";
|
|
6
7
|
import type { Language, Parser, Tree } from "web-tree-sitter";
|
|
7
8
|
import { detectGrammar, resolveGrammarWasmPath } from "../language.ts";
|
|
@@ -12,6 +13,11 @@ import {
|
|
|
12
13
|
type StructuralTimingEvent,
|
|
13
14
|
startStructuralTiming,
|
|
14
15
|
} from "../session/structural-timing.ts";
|
|
16
|
+
import {
|
|
17
|
+
collectSyntaxErrorRanges,
|
|
18
|
+
hasSyntaxErrors,
|
|
19
|
+
withSyntaxCompleteness,
|
|
20
|
+
} from "../syntax-errors.ts";
|
|
15
21
|
import type { GrammarId, QueryCapture, TreeSitterResult } from "../types.ts";
|
|
16
22
|
import {
|
|
17
23
|
ParsedFileReadError,
|
|
@@ -39,6 +45,7 @@ interface ParserEntry {
|
|
|
39
45
|
const QUERY_PARSED_FILE = Symbol("query-parsed-file");
|
|
40
46
|
|
|
41
47
|
interface ParsedQueryInput {
|
|
48
|
+
readonly file: string;
|
|
42
49
|
readonly grammarId: GrammarId;
|
|
43
50
|
readonly tree: Tree;
|
|
44
51
|
readonly source: string;
|
|
@@ -66,8 +73,14 @@ export class TreeSitterRuntime {
|
|
|
66
73
|
constructor(
|
|
67
74
|
private cwd: string,
|
|
68
75
|
private onTiming?: (event: StructuralTimingEvent) => void,
|
|
76
|
+
private readonly fileAssociations?: CFamilyFileAssociationResolver,
|
|
69
77
|
) {}
|
|
70
78
|
|
|
79
|
+
/** Resolve one file's grammar under this runtime's immutable association snapshot. */
|
|
80
|
+
getGrammarForFile(filePath: string): GrammarId | undefined {
|
|
81
|
+
return detectGrammar(filePath, this.fileAssociations);
|
|
82
|
+
}
|
|
83
|
+
|
|
71
84
|
/** Ensure web-tree-sitter Parser is initialized. */
|
|
72
85
|
private async ensureParserInit(): Promise<typeof import("web-tree-sitter")> {
|
|
73
86
|
this.assertActive();
|
|
@@ -152,15 +165,21 @@ export class TreeSitterRuntime {
|
|
|
152
165
|
filePath: string,
|
|
153
166
|
control?: StructuralRequestControl,
|
|
154
167
|
): Promise<
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
168
|
+
| {
|
|
169
|
+
kind: "success";
|
|
170
|
+
data: {
|
|
171
|
+
tree: Tree;
|
|
172
|
+
source: string;
|
|
173
|
+
resolvedPath: string;
|
|
174
|
+
grammarId: GrammarId;
|
|
175
|
+
hasSyntaxErrors: boolean;
|
|
176
|
+
errorRanges: ReturnType<typeof collectSyntaxErrorRanges>;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
| Exclude<TreeSitterResult<never>, { kind: "success" | "partial" }>
|
|
161
180
|
> {
|
|
162
181
|
const resolvedPath = resolveToolPath(this.cwd, filePath);
|
|
163
|
-
const grammarId = detectGrammar(filePath);
|
|
182
|
+
const grammarId = detectGrammar(filePath, this.fileAssociations);
|
|
164
183
|
if (!grammarId) {
|
|
165
184
|
return {
|
|
166
185
|
kind: "unsupported-language",
|
|
@@ -230,6 +249,8 @@ export class TreeSitterRuntime {
|
|
|
230
249
|
source: parsed.source,
|
|
231
250
|
resolvedPath: parsed.resolvedPath,
|
|
232
251
|
grammarId: parsed.grammarId,
|
|
252
|
+
hasSyntaxErrors: hasSyntaxErrors(parsed.tree.rootNode),
|
|
253
|
+
errorRanges: collectSyntaxErrorRanges(parsed.tree.rootNode, parsed.source),
|
|
233
254
|
},
|
|
234
255
|
};
|
|
235
256
|
} catch (err: unknown) {
|
|
@@ -281,7 +302,14 @@ export class TreeSitterRuntime {
|
|
|
281
302
|
|
|
282
303
|
const { grammarId, tree, source } = parseResult.data;
|
|
283
304
|
try {
|
|
284
|
-
return await this[QUERY_PARSED_FILE]({
|
|
305
|
+
return await this[QUERY_PARSED_FILE]({
|
|
306
|
+
file: filePath,
|
|
307
|
+
grammarId,
|
|
308
|
+
tree,
|
|
309
|
+
source,
|
|
310
|
+
queryString,
|
|
311
|
+
control,
|
|
312
|
+
});
|
|
285
313
|
} finally {
|
|
286
314
|
tree.delete();
|
|
287
315
|
}
|
|
@@ -289,7 +317,7 @@ export class TreeSitterRuntime {
|
|
|
289
317
|
|
|
290
318
|
/** Execute a query against one caller-owned parsed tree without parsing again. */
|
|
291
319
|
async [QUERY_PARSED_FILE](input: ParsedQueryInput): Promise<TreeSitterResult<QueryCapture[]>> {
|
|
292
|
-
const { grammarId, tree, source, queryString, control } = input;
|
|
320
|
+
const { file, grammarId, tree, source, queryString, control } = input;
|
|
293
321
|
const validation = validateQueryString(queryString);
|
|
294
322
|
if (validation) return validation;
|
|
295
323
|
|
|
@@ -344,7 +372,13 @@ export class TreeSitterRuntime {
|
|
|
344
372
|
cache: execution.cache,
|
|
345
373
|
finalPhase: "query-execution",
|
|
346
374
|
});
|
|
347
|
-
return {
|
|
375
|
+
return withSyntaxCompleteness({
|
|
376
|
+
data: execution.data,
|
|
377
|
+
file,
|
|
378
|
+
root: tree.rootNode,
|
|
379
|
+
source,
|
|
380
|
+
grammarId,
|
|
381
|
+
});
|
|
348
382
|
} catch (err: unknown) {
|
|
349
383
|
if (isStructuralRequestInterruption(err, control)) {
|
|
350
384
|
finishStructuralTiming(timer, {
|
package/src/worker/service.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { detectGrammar } from "../language.ts";
|
|
2
1
|
import { supportsGrammarOperation } from "../operation-support.ts";
|
|
3
2
|
import type { StructuralWorkerOperation } from "../session/structural-worker-protocol.ts";
|
|
3
|
+
import { withSyntaxCompleteness } from "../syntax-errors.ts";
|
|
4
4
|
import { extractCallSites } from "../tool/call-sites.ts";
|
|
5
5
|
import { lookupCalleesAt } from "../tool/callees.ts";
|
|
6
6
|
import { extractExports } from "../tool/exports.ts";
|
|
@@ -68,16 +68,19 @@ export class StructuralWorkerService {
|
|
|
68
68
|
file: string,
|
|
69
69
|
control: StructuralRequestControl,
|
|
70
70
|
): Promise<TreeSitterResult<unknown>> {
|
|
71
|
-
const unsupported = unsupportedOperation(file, "outline");
|
|
71
|
+
const unsupported = unsupportedOperation(this.runtime, file, "outline");
|
|
72
72
|
if (unsupported) return unsupported;
|
|
73
73
|
const result = await this.runtime.parseFile(file, control);
|
|
74
74
|
if (result.kind !== "success") return result;
|
|
75
75
|
try {
|
|
76
76
|
throwIfStructuralRequestInterrupted(control);
|
|
77
|
-
return {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
return withSyntaxCompleteness({
|
|
78
|
+
data: extractOutline(result.data.tree.rootNode, result.data.source, result.data.grammarId),
|
|
79
|
+
file,
|
|
80
|
+
root: result.data.tree.rootNode,
|
|
81
|
+
source: result.data.source,
|
|
82
|
+
grammarId: result.data.grammarId,
|
|
83
|
+
});
|
|
81
84
|
} finally {
|
|
82
85
|
result.data.tree.delete();
|
|
83
86
|
}
|
|
@@ -87,7 +90,7 @@ export class StructuralWorkerService {
|
|
|
87
90
|
file: string,
|
|
88
91
|
control: StructuralRequestControl,
|
|
89
92
|
): Promise<TreeSitterResult<unknown>> {
|
|
90
|
-
const unsupported = unsupportedOperation(file, "imports");
|
|
93
|
+
const unsupported = unsupportedOperation(this.runtime, file, "imports");
|
|
91
94
|
return unsupported ?? extractImports(this.runtime, file, control);
|
|
92
95
|
}
|
|
93
96
|
|
|
@@ -95,16 +98,17 @@ export class StructuralWorkerService {
|
|
|
95
98
|
file: string,
|
|
96
99
|
control: StructuralRequestControl,
|
|
97
100
|
): Promise<TreeSitterResult<unknown>> {
|
|
98
|
-
const unsupported = unsupportedOperation(file, "exports");
|
|
101
|
+
const unsupported = unsupportedOperation(this.runtime, file, "exports");
|
|
99
102
|
return unsupported ?? extractExports(this.runtime, file, control);
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
105
|
|
|
103
106
|
function unsupportedOperation(
|
|
107
|
+
runtime: TreeSitterRuntime,
|
|
104
108
|
file: string,
|
|
105
109
|
operation: "outline" | "imports" | "exports",
|
|
106
110
|
): TreeSitterResult<never> | null {
|
|
107
|
-
const grammarId =
|
|
111
|
+
const grammarId = runtime.getGrammarForFile(file);
|
|
108
112
|
if (!grammarId || supportsGrammarOperation(grammarId, operation)) return null;
|
|
109
113
|
return {
|
|
110
114
|
kind: "unsupported-language",
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { MessagePort } from "node:worker_threads";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type CFamilyFileAssociationRule,
|
|
4
|
+
createCFamilyFileAssociationResolver,
|
|
5
|
+
isCodeRequestDeadlineError,
|
|
6
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
3
7
|
import type { StructuralTimingEvent } from "../session/structural-timing.ts";
|
|
4
8
|
import { assertStructuralProtocolMessageSize } from "../session/structural-worker-message-size.ts";
|
|
5
9
|
import {
|
|
@@ -18,6 +22,7 @@ import { StructuralWorkerService } from "./service.ts";
|
|
|
18
22
|
interface WorkerBootstrapData {
|
|
19
23
|
readonly cwd: string;
|
|
20
24
|
readonly generation: number;
|
|
25
|
+
readonly fileAssociations?: readonly CFamilyFileAssociationRule[];
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
interface ActiveWorkerRequest {
|
|
@@ -36,7 +41,15 @@ export async function runStructuralWorker(
|
|
|
36
41
|
parentPort: MessagePort,
|
|
37
42
|
workerData: WorkerBootstrapData,
|
|
38
43
|
): Promise<void> {
|
|
39
|
-
const
|
|
44
|
+
const associationResolver = createCFamilyFileAssociationResolver(
|
|
45
|
+
workerData.cwd,
|
|
46
|
+
workerData.fileAssociations ?? [],
|
|
47
|
+
);
|
|
48
|
+
const runtime = new TreeSitterRuntime(
|
|
49
|
+
workerData.cwd,
|
|
50
|
+
(event) => forwardObservation(event),
|
|
51
|
+
associationResolver,
|
|
52
|
+
);
|
|
40
53
|
const service = new StructuralWorkerService(runtime);
|
|
41
54
|
let active: ActiveWorkerRequest | null = null;
|
|
42
55
|
|