@mrclrchtr/supi-tree-sitter 4.8.0 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +3 -1
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +8 -0
- package/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +120 -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 +4 -4
- package/node_modules/web-tree-sitter/package.json +1 -1
- package/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
- package/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +4 -4
- package/package.json +4 -3
- package/resources/grammars/kotlin/tree-sitter-kotlin.wasm.json +1 -1
- package/resources/grammars/sql/tree-sitter-sql.wasm.json +1 -1
- package/src/session/runtime.ts +106 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-tree-sitter",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.0",
|
|
4
4
|
"description": "Structural AST analysis for SuPi code intelligence",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"web-tree-sitter": "^0.26.8",
|
|
37
|
-
"@mrclrchtr/supi-code-runtime": "4.
|
|
38
|
-
"@mrclrchtr/supi-core": "4.
|
|
37
|
+
"@mrclrchtr/supi-code-runtime": "4.10.0",
|
|
38
|
+
"@mrclrchtr/supi-core": "4.10.0"
|
|
39
39
|
},
|
|
40
40
|
"bundledDependencies": [
|
|
41
41
|
"@mrclrchtr/supi-code-runtime",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"./provider/tree-sitter-provider": "./src/provider/tree-sitter-provider.ts"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|
|
52
|
+
"bench:structural": "vitest bench __tests__/benchmark/structural-baseline.bench.ts --run",
|
|
52
53
|
"vendor:wasm": "node scripts/vendor-wasm.mjs",
|
|
53
54
|
"check:wasm": "node scripts/vendor-wasm.mjs --check",
|
|
54
55
|
"generate:kotlin-wasm": "node scripts/generate-kotlin-wasm.mjs",
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"releaseAsset": "https://github.com/fwcd/tree-sitter-kotlin/releases/download/0.3.8/tree-sitter-kotlin.wasm"
|
|
7
7
|
},
|
|
8
8
|
"generatedWith": {
|
|
9
|
-
"treeSitterCli": "0.26.
|
|
9
|
+
"treeSitterCli": "0.26.12"
|
|
10
10
|
},
|
|
11
11
|
"sha256": "e08447e36ad5d86f39f91f13defce0636aea1490b8d65dafde6fa72649422061"
|
|
12
12
|
}
|
package/src/session/runtime.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// Tree-sitter runtime — parser management, parse, and query services.
|
|
2
2
|
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
3
4
|
import * as fs from "node:fs";
|
|
4
5
|
import * as path from "node:path";
|
|
6
|
+
import { startDebugTimer } from "@mrclrchtr/supi-core/debug";
|
|
5
7
|
import { resolveToolPath } from "@mrclrchtr/supi-core/path";
|
|
6
8
|
import type { Language, Parser, Tree } from "web-tree-sitter";
|
|
7
9
|
import { nodeToRange } from "../coordinates.ts";
|
|
@@ -126,11 +128,25 @@ export class TreeSitterRuntime {
|
|
|
126
128
|
};
|
|
127
129
|
}
|
|
128
130
|
|
|
131
|
+
const timer = startDebugTimer();
|
|
132
|
+
const parserState = this.parsers.has(grammarId)
|
|
133
|
+
? "reused"
|
|
134
|
+
: this.parserPromises.has(grammarId)
|
|
135
|
+
? "initializing"
|
|
136
|
+
: "cold";
|
|
137
|
+
|
|
129
138
|
// Read the file
|
|
130
139
|
let source: string;
|
|
131
140
|
try {
|
|
132
141
|
source = fs.readFileSync(resolvedPath, "utf-8");
|
|
133
142
|
} catch (err: unknown) {
|
|
143
|
+
finishStructuralTiming(timer, {
|
|
144
|
+
operation: "parse",
|
|
145
|
+
grammar: grammarId,
|
|
146
|
+
parserState,
|
|
147
|
+
outcome: "file-access-error",
|
|
148
|
+
finalPhase: "file-read",
|
|
149
|
+
});
|
|
134
150
|
const message = err instanceof Error ? err.message : "File could not be read";
|
|
135
151
|
return {
|
|
136
152
|
kind: "file-access-error",
|
|
@@ -138,15 +154,42 @@ export class TreeSitterRuntime {
|
|
|
138
154
|
message,
|
|
139
155
|
};
|
|
140
156
|
}
|
|
157
|
+
timer.mark("file-read");
|
|
158
|
+
|
|
159
|
+
if (timer.enabled) {
|
|
160
|
+
try {
|
|
161
|
+
createHash("sha256").update(source).digest();
|
|
162
|
+
} catch {
|
|
163
|
+
// Timing-only hashing must not change structural behavior.
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
timer.mark("content-hash");
|
|
141
167
|
|
|
168
|
+
let parserReady = false;
|
|
142
169
|
try {
|
|
143
170
|
const entry = await this.ensureGrammarParser(grammarId);
|
|
171
|
+
timer.mark("parser-setup");
|
|
172
|
+
parserReady = true;
|
|
144
173
|
const tree = entry.parser.parse(source);
|
|
174
|
+
finishStructuralTiming(timer, {
|
|
175
|
+
operation: "parse",
|
|
176
|
+
grammar: grammarId,
|
|
177
|
+
parserState,
|
|
178
|
+
outcome: "completed",
|
|
179
|
+
finalPhase: "parse",
|
|
180
|
+
});
|
|
145
181
|
return {
|
|
146
182
|
kind: "success",
|
|
147
183
|
data: { tree: tree as Tree, source, resolvedPath, grammarId },
|
|
148
184
|
};
|
|
149
185
|
} catch (err: unknown) {
|
|
186
|
+
finishStructuralTiming(timer, {
|
|
187
|
+
operation: "parse",
|
|
188
|
+
grammar: grammarId,
|
|
189
|
+
parserState,
|
|
190
|
+
outcome: "runtime-error",
|
|
191
|
+
finalPhase: parserReady ? "parse" : "parser-setup",
|
|
192
|
+
});
|
|
150
193
|
return { kind: "runtime-error", message: formatError(err, "Parser initialization failed") };
|
|
151
194
|
}
|
|
152
195
|
}
|
|
@@ -169,16 +212,27 @@ export class TreeSitterRuntime {
|
|
|
169
212
|
const parseResult = await this.parseFile(filePath);
|
|
170
213
|
if (parseResult.kind !== "success") return parseResult;
|
|
171
214
|
|
|
172
|
-
const { tree, source } = parseResult.data;
|
|
215
|
+
const { grammarId, tree, source } = parseResult.data;
|
|
216
|
+
const timer = startDebugTimer();
|
|
217
|
+
let compiled = false;
|
|
173
218
|
|
|
174
219
|
try {
|
|
175
|
-
const entry = await this.ensureGrammarParser(
|
|
220
|
+
const entry = await this.ensureGrammarParser(grammarId);
|
|
176
221
|
const mod = await this.ensureParserInit();
|
|
177
222
|
let query: InstanceType<typeof mod.Query>;
|
|
178
223
|
|
|
179
224
|
try {
|
|
180
225
|
query = new mod.Query(entry.language, queryString);
|
|
226
|
+
compiled = true;
|
|
227
|
+
timer.mark("query-compilation");
|
|
181
228
|
} catch (err: unknown) {
|
|
229
|
+
finishStructuralTiming(timer, {
|
|
230
|
+
operation: "query",
|
|
231
|
+
grammar: grammarId,
|
|
232
|
+
outcome: "validation-error",
|
|
233
|
+
captureCount: 0,
|
|
234
|
+
finalPhase: "query-compilation",
|
|
235
|
+
});
|
|
182
236
|
return { kind: "validation-error", message: `Invalid query: ${formatError(err)}` };
|
|
183
237
|
}
|
|
184
238
|
|
|
@@ -195,11 +249,25 @@ export class TreeSitterRuntime {
|
|
|
195
249
|
});
|
|
196
250
|
}
|
|
197
251
|
}
|
|
252
|
+
finishStructuralTiming(timer, {
|
|
253
|
+
operation: "query",
|
|
254
|
+
grammar: grammarId,
|
|
255
|
+
outcome: "completed",
|
|
256
|
+
captureCount: captures.length,
|
|
257
|
+
finalPhase: "query-execution",
|
|
258
|
+
});
|
|
198
259
|
return { kind: "success", data: captures };
|
|
199
260
|
} finally {
|
|
200
261
|
query.delete();
|
|
201
262
|
}
|
|
202
263
|
} catch (err: unknown) {
|
|
264
|
+
finishStructuralTiming(timer, {
|
|
265
|
+
operation: "query",
|
|
266
|
+
grammar: grammarId,
|
|
267
|
+
outcome: "runtime-error",
|
|
268
|
+
captureCount: 0,
|
|
269
|
+
finalPhase: compiled ? "query-execution" : "query-compilation",
|
|
270
|
+
});
|
|
203
271
|
return { kind: "runtime-error", message: formatError(err, "Query execution failed") };
|
|
204
272
|
} finally {
|
|
205
273
|
tree.delete();
|
|
@@ -239,6 +307,42 @@ export class TreeSitterRuntime {
|
|
|
239
307
|
/** Max query string length to prevent ReDoS via overly complex Tree-sitter patterns. */
|
|
240
308
|
const MAX_QUERY_LENGTH = 10_000;
|
|
241
309
|
|
|
310
|
+
type StructuralTimer = ReturnType<typeof startDebugTimer>;
|
|
311
|
+
type ParserState = "cold" | "initializing" | "reused";
|
|
312
|
+
|
|
313
|
+
type StructuralTimingObservation =
|
|
314
|
+
| {
|
|
315
|
+
readonly operation: "parse";
|
|
316
|
+
readonly grammar: GrammarId;
|
|
317
|
+
readonly parserState: ParserState;
|
|
318
|
+
readonly outcome: "completed" | "file-access-error" | "runtime-error";
|
|
319
|
+
readonly finalPhase: "file-read" | "parse" | "parser-setup";
|
|
320
|
+
}
|
|
321
|
+
| {
|
|
322
|
+
readonly operation: "query";
|
|
323
|
+
readonly grammar: GrammarId;
|
|
324
|
+
readonly outcome: "completed" | "runtime-error" | "validation-error";
|
|
325
|
+
readonly captureCount: number;
|
|
326
|
+
readonly finalPhase: "query-compilation" | "query-execution";
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
function finishStructuralTiming(
|
|
330
|
+
timer: StructuralTimer,
|
|
331
|
+
observation: StructuralTimingObservation,
|
|
332
|
+
): void {
|
|
333
|
+
const { finalPhase, ...data } = observation;
|
|
334
|
+
timer.finish(
|
|
335
|
+
() => ({
|
|
336
|
+
source: "tree-sitter",
|
|
337
|
+
level: "debug",
|
|
338
|
+
category: `structural.${data.operation}.timing`,
|
|
339
|
+
message: `Tree-sitter ${data.operation} ${data.outcome}`,
|
|
340
|
+
data: { ...data },
|
|
341
|
+
}),
|
|
342
|
+
finalPhase,
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
242
346
|
/** Format errors with their cause chain's first message for user-facing tool output. */
|
|
243
347
|
function formatError(err: unknown, fallback = "Operation failed"): string {
|
|
244
348
|
if (!(err instanceof Error)) return String(err || fallback);
|