@functional-systems/srcx 0.1.0-dev.3 → 0.1.0-dev.31
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/CHANGELOG.md +137 -1
- package/DESIGN.md +193 -58
- package/README.md +553 -38
- package/RELEASING.md +30 -9
- package/VALIDATION.md +855 -25
- package/dist/build.d.ts +81 -1
- package/dist/build.js +187 -37
- package/dist/build.js.map +1 -1
- package/dist/chunk.d.ts +9 -3
- package/dist/chunk.js +341 -14
- package/dist/chunk.js.map +1 -1
- package/dist/cli.js +103 -19
- package/dist/cli.js.map +1 -1
- package/dist/file-policy.d.ts +20 -0
- package/dist/file-policy.js +96 -0
- package/dist/file-policy.js.map +1 -0
- package/dist/implementation.d.ts +11 -0
- package/dist/implementation.js +191 -0
- package/dist/implementation.js.map +1 -0
- package/dist/publish.d.ts +18 -0
- package/dist/publish.js +158 -34
- package/dist/publish.js.map +1 -1
- package/dist/releases.js +21 -3
- package/dist/releases.js.map +1 -1
- package/dist/remote.d.ts +1 -0
- package/dist/remote.js +50 -9
- package/dist/remote.js.map +1 -1
- package/dist/repository.d.ts +4 -1
- package/dist/repository.js +26 -11
- package/dist/repository.js.map +1 -1
- package/dist/rerank.d.ts +16 -0
- package/dist/rerank.js +87 -0
- package/dist/rerank.js.map +1 -0
- package/dist/search.d.ts +14 -4
- package/dist/search.js +119 -23
- package/dist/search.js.map +1 -1
- package/dist/skills.d.ts +24 -0
- package/dist/skills.js +155 -0
- package/dist/skills.js.map +1 -0
- package/package.json +16 -5
- package/runtime/grammars/README.md +50 -0
- package/runtime/grammars/tree-sitter-bash.LICENSE +21 -0
- package/runtime/grammars/tree-sitter-bash.wasm +0 -0
- package/runtime/grammars/tree-sitter-sql.LICENSE +21 -0
- package/runtime/grammars/tree-sitter-sql.wasm +0 -0
- package/runtime/grammars/tree-sitter-wasm.LICENSE +21 -0
- package/runtime/qwen.py +132 -0
- package/runtime/requirements.txt +34 -0
- package/skills/srcx-search/SKILL.md +85 -0
- package/src/build.ts +258 -47
- package/src/chunk.ts +360 -12
- package/src/cli.ts +212 -24
- package/src/file-policy.ts +157 -0
- package/src/implementation.ts +253 -0
- package/src/publish.ts +226 -34
- package/src/releases.ts +36 -3
- package/src/remote.ts +70 -13
- package/src/repository.ts +40 -11
- package/src/rerank.ts +131 -0
- package/src/search.ts +154 -28
- package/src/skills.ts +205 -0
package/src/chunk.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
3
|
import { Parser, Language, type Node } from "web-tree-sitter";
|
|
3
4
|
import { getEncoding } from "js-tiktoken";
|
|
@@ -8,15 +9,19 @@ export function tokens(text: string): number {
|
|
|
8
9
|
return encoding.encode(text, [], []).length;
|
|
9
10
|
}
|
|
10
11
|
export const CHUNKER = {
|
|
11
|
-
version:
|
|
12
|
+
version: 2,
|
|
12
13
|
parser: "web-tree-sitter@0.25.10",
|
|
13
14
|
grammars: "tree-sitter-wasms@0.1.13",
|
|
15
|
+
sqlGrammar:
|
|
16
|
+
"tree-sitter-wasm@2.0.2/sql/b77530893b1dd6d1d4814eacf34d25bcbe4a12ec9a25a8c45b320d447265f42b",
|
|
17
|
+
shellGrammar:
|
|
18
|
+
"tree-sitter-wasm@2.0.2/bash/5daf3f2ac1cea01a64cfd6878ef7d247310925a4c62238eb4b8c1feb5dce355a",
|
|
14
19
|
tokenizer: "js-tiktoken@1.0.21/cl100k_base",
|
|
15
20
|
targetTokens: 800,
|
|
16
21
|
maxTokens: 1500,
|
|
17
22
|
fallbackOverlap: 0.1,
|
|
18
23
|
enrichment: "path-scope-symbol-v1",
|
|
19
|
-
policy: "source-
|
|
24
|
+
policy: "source-v2",
|
|
20
25
|
} as const;
|
|
21
26
|
export type Span = {
|
|
22
27
|
startByte: number;
|
|
@@ -46,17 +51,40 @@ async function language(name: string): Promise<Language> {
|
|
|
46
51
|
let l = languages.get(name);
|
|
47
52
|
if (!l) {
|
|
48
53
|
l = await Language.load(
|
|
49
|
-
|
|
54
|
+
name === "sql" || name === "shell"
|
|
55
|
+
? fileURLToPath(
|
|
56
|
+
new URL(
|
|
57
|
+
`../runtime/grammars/tree-sitter-${name === "shell" ? "bash" : "sql"}.wasm`,
|
|
58
|
+
import.meta.url,
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
: require.resolve(`tree-sitter-wasms/out/tree-sitter-${name}.wasm`),
|
|
50
62
|
);
|
|
51
63
|
languages.set(name, l);
|
|
52
64
|
}
|
|
53
65
|
return l;
|
|
54
66
|
}
|
|
55
67
|
export function detectLanguage(path: string): string {
|
|
68
|
+
if (path.endsWith(".C")) return "cpp";
|
|
56
69
|
const ext = path.split(".").at(-1)?.toLowerCase();
|
|
57
70
|
return (
|
|
58
71
|
(
|
|
59
72
|
{
|
|
73
|
+
py: "python",
|
|
74
|
+
pyi: "python",
|
|
75
|
+
go: "go",
|
|
76
|
+
rs: "rust",
|
|
77
|
+
c: "c",
|
|
78
|
+
h: "c",
|
|
79
|
+
cc: "cpp",
|
|
80
|
+
cpp: "cpp",
|
|
81
|
+
cxx: "cpp",
|
|
82
|
+
hh: "cpp",
|
|
83
|
+
hpp: "cpp",
|
|
84
|
+
hxx: "cpp",
|
|
85
|
+
sh: "shell",
|
|
86
|
+
bash: "shell",
|
|
87
|
+
sql: "sql",
|
|
60
88
|
java: "java",
|
|
61
89
|
ts: "typescript",
|
|
62
90
|
tsx: "tsx",
|
|
@@ -75,7 +103,9 @@ export function detectLanguage(path: string): string {
|
|
|
75
103
|
)[ext ?? ""] ?? "text"
|
|
76
104
|
);
|
|
77
105
|
}
|
|
78
|
-
|
|
106
|
+
export type Enrichment = "path-scope-symbol-v1" | "path-only-v1";
|
|
107
|
+
function enrichment(path: string, u: Unit, policy: Enrichment): string {
|
|
108
|
+
if (policy === "path-only-v1") return path.slice(0, 240) + "\n";
|
|
79
109
|
return (
|
|
80
110
|
[path.slice(0, 240), u.scope?.slice(0, 160), u.symbol?.slice(0, 160)]
|
|
81
111
|
.filter(Boolean)
|
|
@@ -100,16 +130,289 @@ function scopeName(n: Node): string | undefined {
|
|
|
100
130
|
}
|
|
101
131
|
return names.length ? names.join(".") : undefined;
|
|
102
132
|
}
|
|
133
|
+
|
|
134
|
+
/** Keep complete functions (including decorators) before token-bound splitting. */
|
|
135
|
+
function pythonUnits(root: Node): Unit[] {
|
|
136
|
+
const units: Unit[] = [];
|
|
137
|
+
function visit(n: Node, scope?: string): void {
|
|
138
|
+
const definition = n.childForFieldName("definition") ?? n;
|
|
139
|
+
const body = definition.childForFieldName("body");
|
|
140
|
+
const symbol = definition.childForFieldName("name")?.text.slice(0, 200);
|
|
141
|
+
const unit: Unit = {
|
|
142
|
+
start: n.startIndex,
|
|
143
|
+
end: n.endIndex,
|
|
144
|
+
kind: classify(definition.type),
|
|
145
|
+
scope,
|
|
146
|
+
symbol,
|
|
147
|
+
};
|
|
148
|
+
if (definition.type === "class_definition" && body) {
|
|
149
|
+
units.push({ ...unit, end: body.startIndex });
|
|
150
|
+
const nested = [scope, symbol].filter(Boolean).join(".");
|
|
151
|
+
for (const child of body.namedChildren) if (child) visit(child, nested);
|
|
152
|
+
} else {
|
|
153
|
+
if (definition.type === "function_definition" && body)
|
|
154
|
+
unit.signature = definition.text
|
|
155
|
+
.slice(0, body.startIndex - definition.startIndex)
|
|
156
|
+
.trim()
|
|
157
|
+
.slice(0, 240);
|
|
158
|
+
if (
|
|
159
|
+
definition.type === "expression_statement" &&
|
|
160
|
+
definition.firstNamedChild?.type === "string"
|
|
161
|
+
)
|
|
162
|
+
unit.kind = "documentation";
|
|
163
|
+
units.push(unit);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
for (const n of root.namedChildren) if (n) visit(n);
|
|
167
|
+
return units;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function goUnits(root: Node): Unit[] {
|
|
171
|
+
const units: Unit[] = [];
|
|
172
|
+
for (const n of root.namedChildren) {
|
|
173
|
+
if (!n) continue;
|
|
174
|
+
if (n.type === "type_declaration") {
|
|
175
|
+
// Include the type keyword and grouped declaration delimiters in coverage.
|
|
176
|
+
const specs = n.namedChildren.filter(
|
|
177
|
+
(node): node is Node => node !== null,
|
|
178
|
+
);
|
|
179
|
+
for (const [i, spec] of specs.entries())
|
|
180
|
+
units.push({
|
|
181
|
+
start: i === 0 ? n.startIndex : spec.startIndex,
|
|
182
|
+
end: i === specs.length - 1 ? n.endIndex : spec.endIndex,
|
|
183
|
+
kind: classify(spec.type),
|
|
184
|
+
symbol: spec.childForFieldName("name")?.text.slice(0, 200),
|
|
185
|
+
});
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const unit: Unit = {
|
|
189
|
+
start: n.startIndex,
|
|
190
|
+
end: n.endIndex,
|
|
191
|
+
kind: classify(n.type),
|
|
192
|
+
symbol: n.childForFieldName("name")?.text.slice(0, 200),
|
|
193
|
+
};
|
|
194
|
+
let receiver = n
|
|
195
|
+
.childForFieldName("receiver")
|
|
196
|
+
?.firstNamedChild?.childForFieldName("type");
|
|
197
|
+
if (receiver?.type === "pointer_type") receiver = receiver.firstNamedChild;
|
|
198
|
+
if (receiver?.type === "generic_type")
|
|
199
|
+
receiver = receiver.childForFieldName("type");
|
|
200
|
+
if (receiver) unit.scope = receiver.text.slice(0, 160);
|
|
201
|
+
const body = n.childForFieldName("body");
|
|
202
|
+
if (unit.kind === "function")
|
|
203
|
+
unit.signature = n.text
|
|
204
|
+
.slice(0, body ? body.startIndex - n.startIndex : undefined)
|
|
205
|
+
.trim()
|
|
206
|
+
.slice(0, 240);
|
|
207
|
+
units.push(unit);
|
|
208
|
+
}
|
|
209
|
+
return units;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Walk declarators rather than parameter/type names (including pointer returns). */
|
|
213
|
+
function declaratorName(n: Node | null): string | undefined {
|
|
214
|
+
if (!n) return undefined;
|
|
215
|
+
if (
|
|
216
|
+
/^(identifier|field_identifier|type_identifier|qualified_identifier|destructor_name|operator_name)$/.test(
|
|
217
|
+
n.type,
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
return n.text;
|
|
221
|
+
return declaratorName(
|
|
222
|
+
n.childForFieldName("declarator") ??
|
|
223
|
+
(n.type === "parenthesized_declarator" ? n.firstNamedChild : null),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function cUnits(root: Node): Unit[] {
|
|
228
|
+
const units: Unit[] = [];
|
|
229
|
+
function visit(n: Node, scope?: string, start = n.startIndex): void {
|
|
230
|
+
if (n.type === "template_declaration") {
|
|
231
|
+
const declaration = n.namedChildren
|
|
232
|
+
.filter((c): c is Node => !!c && c.type !== "template_parameter_list")
|
|
233
|
+
.at(-1);
|
|
234
|
+
if (declaration) {
|
|
235
|
+
visit(declaration, scope, start);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const body = n.childForFieldName("body");
|
|
240
|
+
const symbol =
|
|
241
|
+
n.childForFieldName("name")?.text ??
|
|
242
|
+
declaratorName(n.childForFieldName("declarator"));
|
|
243
|
+
const unit: Unit = {
|
|
244
|
+
start,
|
|
245
|
+
end: n.endIndex,
|
|
246
|
+
kind: n.type === "preproc_include" ? "imports" : classify(n.type),
|
|
247
|
+
scope,
|
|
248
|
+
symbol: symbol?.slice(0, 200),
|
|
249
|
+
};
|
|
250
|
+
if (body && /^(declaration_list|field_declaration_list)$/.test(body.type)) {
|
|
251
|
+
const nested = [scope, symbol].filter(Boolean).join("::") || undefined;
|
|
252
|
+
units.push({ ...unit, end: body.startIndex + 1 });
|
|
253
|
+
for (const child of body.namedChildren) if (child) visit(child, nested);
|
|
254
|
+
units.push({
|
|
255
|
+
start: body.endIndex - 1,
|
|
256
|
+
end: n.endIndex,
|
|
257
|
+
kind: "structural",
|
|
258
|
+
scope: nested,
|
|
259
|
+
});
|
|
260
|
+
} else if (/^preproc_(if|ifdef|elif|else|elifdef)$/.test(n.type)) {
|
|
261
|
+
// Preserve directives and both branches without evaluating the build configuration.
|
|
262
|
+
const condition =
|
|
263
|
+
n.childForFieldName("condition") ?? n.childForFieldName("name");
|
|
264
|
+
const children = n.namedChildren.filter(
|
|
265
|
+
(c): c is Node => !!c && c.id !== condition?.id,
|
|
266
|
+
);
|
|
267
|
+
if (!children.length) {
|
|
268
|
+
units.push(unit);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
units.push({ ...unit, end: children[0]!.startIndex });
|
|
272
|
+
for (const child of children) visit(child, scope);
|
|
273
|
+
const end = children.at(-1)!.endIndex;
|
|
274
|
+
if (end < n.endIndex)
|
|
275
|
+
units.push({ start: end, end: n.endIndex, kind: "structural", scope });
|
|
276
|
+
} else {
|
|
277
|
+
if (n.type === "function_definition")
|
|
278
|
+
unit.signature = n.text
|
|
279
|
+
.slice(0, body ? body.startIndex - n.startIndex : undefined)
|
|
280
|
+
.trim()
|
|
281
|
+
.slice(0, 240);
|
|
282
|
+
units.push(unit);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
for (const n of root.namedChildren) if (n) visit(n);
|
|
286
|
+
return units;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function shellUnits(root: Node): Unit[] {
|
|
290
|
+
return root.namedChildren
|
|
291
|
+
.filter((n): n is Node => !!n)
|
|
292
|
+
.map((n) => {
|
|
293
|
+
const body = n.childForFieldName("body");
|
|
294
|
+
return {
|
|
295
|
+
start: n.startIndex,
|
|
296
|
+
end: n.endIndex,
|
|
297
|
+
kind: classify(n.type),
|
|
298
|
+
symbol:
|
|
299
|
+
n.type === "function_definition"
|
|
300
|
+
? n.childForFieldName("name")?.text.slice(0, 200)
|
|
301
|
+
: undefined,
|
|
302
|
+
signature:
|
|
303
|
+
n.type === "function_definition"
|
|
304
|
+
? n.text
|
|
305
|
+
.slice(0, body ? body.startIndex - n.startIndex : undefined)
|
|
306
|
+
.trim()
|
|
307
|
+
.slice(0, 240)
|
|
308
|
+
: undefined,
|
|
309
|
+
};
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function sqlUnits(root: Node): Unit[] {
|
|
314
|
+
return root.namedChildren
|
|
315
|
+
.filter((n): n is Node => !!n)
|
|
316
|
+
.map((n) => {
|
|
317
|
+
const declaration = n.firstNamedChild;
|
|
318
|
+
// Object names describe DDL declarations, not every table referenced by a query.
|
|
319
|
+
const object = declaration?.type.startsWith("create_")
|
|
320
|
+
? declaration.namedChildren.find((c) => c?.type === "object_reference")
|
|
321
|
+
: undefined;
|
|
322
|
+
return {
|
|
323
|
+
start: n.startIndex,
|
|
324
|
+
end: n.nextSibling?.type === ";" ? n.nextSibling.endIndex : n.endIndex,
|
|
325
|
+
kind: n.type === "comment" ? "documentation" : "statement",
|
|
326
|
+
symbol: object?.childForFieldName("name")?.text.slice(0, 200),
|
|
327
|
+
scope: object?.childForFieldName("schema")?.text.slice(0, 160),
|
|
328
|
+
};
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function rustUnits(root: Node): Unit[] {
|
|
333
|
+
const units: Unit[] = [];
|
|
334
|
+
function visit(container: Node, scope?: string): void {
|
|
335
|
+
let prefix: number | undefined;
|
|
336
|
+
for (const n of container.namedChildren) {
|
|
337
|
+
if (!n) continue;
|
|
338
|
+
// Outer attributes/doc comments belong to the following item, not a chunk
|
|
339
|
+
// containing only cfg/derive metadata. Inner module docs remain separate.
|
|
340
|
+
if (
|
|
341
|
+
n.type === "attribute_item" ||
|
|
342
|
+
/^(\/\/\/(?!\/)|\/\*\*(?!\*))/.test(n.text)
|
|
343
|
+
) {
|
|
344
|
+
prefix ??= n.startIndex;
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (prefix !== undefined && n.type.endsWith("comment")) continue;
|
|
348
|
+
const body = n.childForFieldName("body");
|
|
349
|
+
const name = n.childForFieldName("name")?.text;
|
|
350
|
+
const type = n.childForFieldName("type")?.text;
|
|
351
|
+
const trait = n.childForFieldName("trait")?.text;
|
|
352
|
+
const unit: Unit = {
|
|
353
|
+
start: prefix ?? n.startIndex,
|
|
354
|
+
end: n.endIndex,
|
|
355
|
+
kind:
|
|
356
|
+
n.type === "use_declaration" || n.type === "extern_crate_declaration"
|
|
357
|
+
? "imports"
|
|
358
|
+
: classify(n.type),
|
|
359
|
+
scope,
|
|
360
|
+
symbol: (
|
|
361
|
+
name ??
|
|
362
|
+
(n.type === "impl_item" ? type : n.childForFieldName("macro")?.text)
|
|
363
|
+
)?.slice(0, 200),
|
|
364
|
+
};
|
|
365
|
+
prefix = undefined;
|
|
366
|
+
if (body?.type === "declaration_list") {
|
|
367
|
+
const owner =
|
|
368
|
+
n.type === "impl_item"
|
|
369
|
+
? [type, trait].filter(Boolean).join(" as ")
|
|
370
|
+
: name;
|
|
371
|
+
const nested =
|
|
372
|
+
[scope, owner].filter(Boolean).join("::").slice(0, 160) || undefined;
|
|
373
|
+
units.push({ ...unit, end: body.startIndex + 1 });
|
|
374
|
+
visit(body, nested);
|
|
375
|
+
// Keep closing delimiters with their container, including empty bodies.
|
|
376
|
+
units.push({
|
|
377
|
+
start: body.endIndex - 1,
|
|
378
|
+
end: n.endIndex,
|
|
379
|
+
kind: "structural",
|
|
380
|
+
scope: nested,
|
|
381
|
+
});
|
|
382
|
+
} else {
|
|
383
|
+
if (unit.kind === "function")
|
|
384
|
+
unit.signature = n.text
|
|
385
|
+
.slice(0, body ? body.startIndex - n.startIndex : undefined)
|
|
386
|
+
.trim()
|
|
387
|
+
.slice(0, 240);
|
|
388
|
+
units.push(unit);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (prefix !== undefined)
|
|
392
|
+
units.push({
|
|
393
|
+
start: prefix,
|
|
394
|
+
end:
|
|
395
|
+
container.type === "declaration_list"
|
|
396
|
+
? container.endIndex - 1
|
|
397
|
+
: container.endIndex,
|
|
398
|
+
kind: "documentation",
|
|
399
|
+
scope,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
visit(root);
|
|
403
|
+
return units;
|
|
404
|
+
}
|
|
103
405
|
/** Bound strings without slicing a surrogate pair; prefer line ends where possible. */
|
|
104
406
|
function splitUnit(
|
|
105
407
|
source: string,
|
|
106
408
|
path: string,
|
|
107
409
|
u: Unit,
|
|
108
410
|
overlap: number,
|
|
411
|
+
policy: Enrichment,
|
|
109
412
|
): Unit[] {
|
|
110
413
|
const result: Unit[] = [];
|
|
111
414
|
let start = u.start;
|
|
112
|
-
const prefix = enrichment(path, u);
|
|
415
|
+
const prefix = enrichment(path, u, policy);
|
|
113
416
|
invariant(
|
|
114
417
|
tokens(prefix) < CHUNKER.maxTokens,
|
|
115
418
|
"Path/scope enrichment exceeds token limit.",
|
|
@@ -185,6 +488,7 @@ export async function chunk(
|
|
|
185
488
|
source: string,
|
|
186
489
|
path: string,
|
|
187
490
|
mode: "syntax" | "window" = "syntax",
|
|
491
|
+
policy: Enrichment = CHUNKER.enrichment,
|
|
188
492
|
): Promise<{ spans: Span[]; parseStatus: string; language: string }> {
|
|
189
493
|
const lang = detectLanguage(path);
|
|
190
494
|
if (!source.length)
|
|
@@ -194,7 +498,21 @@ export async function chunk(
|
|
|
194
498
|
if (mode === "window") {
|
|
195
499
|
status = "window-baseline";
|
|
196
500
|
units = [{ start: 0, end: source.length, kind: "fallback" }];
|
|
197
|
-
} else if (
|
|
501
|
+
} else if (
|
|
502
|
+
[
|
|
503
|
+
"java",
|
|
504
|
+
"typescript",
|
|
505
|
+
"tsx",
|
|
506
|
+
"javascript",
|
|
507
|
+
"python",
|
|
508
|
+
"go",
|
|
509
|
+
"rust",
|
|
510
|
+
"c",
|
|
511
|
+
"cpp",
|
|
512
|
+
"shell",
|
|
513
|
+
"sql",
|
|
514
|
+
].includes(lang)
|
|
515
|
+
) {
|
|
198
516
|
const grammar = await language(lang);
|
|
199
517
|
const parser = new Parser();
|
|
200
518
|
let tree: ReturnType<Parser["parse"]> = null;
|
|
@@ -224,12 +542,18 @@ export async function chunk(
|
|
|
224
542
|
if (
|
|
225
543
|
n.namedChildCount &&
|
|
226
544
|
(container ||
|
|
227
|
-
tokens(enrichment(path, u) + n.text) > CHUNKER.maxTokens)
|
|
545
|
+
tokens(enrichment(path, u, policy) + n.text) > CHUNKER.maxTokens)
|
|
228
546
|
) {
|
|
229
547
|
for (const child of n.namedChildren) if (child) visit(child);
|
|
230
548
|
} else if (n.endIndex > n.startIndex) units.push(u);
|
|
231
549
|
}
|
|
232
|
-
|
|
550
|
+
if (lang === "python") units = pythonUnits(tree.rootNode);
|
|
551
|
+
else if (lang === "go") units = goUnits(tree.rootNode);
|
|
552
|
+
else if (lang === "rust") units = rustUnits(tree.rootNode);
|
|
553
|
+
else if (lang === "c" || lang === "cpp") units = cUnits(tree.rootNode);
|
|
554
|
+
else if (lang === "shell") units = shellUnits(tree.rootNode);
|
|
555
|
+
else if (lang === "sql") units = sqlUnits(tree.rootNode);
|
|
556
|
+
else visit(tree.rootNode);
|
|
233
557
|
// Attach every gap (punctuation, BOM, comments, whitespace) without altering source bytes.
|
|
234
558
|
let cursor = 0;
|
|
235
559
|
const covered: Unit[] = [];
|
|
@@ -268,11 +592,15 @@ export async function chunk(
|
|
|
268
592
|
const prev = merged.at(-1);
|
|
269
593
|
if (
|
|
270
594
|
prev &&
|
|
595
|
+
lang !== "sql" &&
|
|
596
|
+
(!["python", "go", "rust", "c", "cpp", "shell"].includes(lang) ||
|
|
597
|
+
(!prev.symbol && !u.symbol)) &&
|
|
271
598
|
prev.kind !== "function" &&
|
|
272
599
|
u.kind === prev.kind &&
|
|
273
600
|
u.scope === prev.scope &&
|
|
274
|
-
tokens(
|
|
275
|
-
|
|
601
|
+
tokens(
|
|
602
|
+
enrichment(path, prev, policy) + source.slice(prev.start, u.end),
|
|
603
|
+
) <= CHUNKER.targetTokens
|
|
276
604
|
) {
|
|
277
605
|
prev.end = u.end;
|
|
278
606
|
delete prev.symbol;
|
|
@@ -280,7 +608,7 @@ export async function chunk(
|
|
|
280
608
|
} else merged.push({ ...u });
|
|
281
609
|
}
|
|
282
610
|
const bounded = merged.flatMap((u) =>
|
|
283
|
-
splitUnit(source, path, u, fallback ? CHUNKER.fallbackOverlap : 0),
|
|
611
|
+
splitUnit(source, path, u, fallback ? CHUNKER.fallbackOverlap : 0, policy),
|
|
284
612
|
);
|
|
285
613
|
const offsets = new Uint32Array(source.length + 1);
|
|
286
614
|
let byte = 0;
|
|
@@ -298,7 +626,7 @@ export async function chunk(
|
|
|
298
626
|
const startByte = offsets[u.start]!,
|
|
299
627
|
endByte = offsets[u.end]!;
|
|
300
628
|
const text = source.slice(u.start, u.end);
|
|
301
|
-
const searchText = enrichment(path, u) + text;
|
|
629
|
+
const searchText = enrichment(path, u, policy) + text;
|
|
302
630
|
const kind = text.trim() === "" ? "structural" : u.kind;
|
|
303
631
|
return {
|
|
304
632
|
startByte,
|
|
@@ -326,3 +654,23 @@ export async function chunk(
|
|
|
326
654
|
invariant(covered === raw.length, "Incomplete source coverage.");
|
|
327
655
|
return { spans, parseStatus: status, language: lang };
|
|
328
656
|
}
|
|
657
|
+
|
|
658
|
+
/** Inspect a stored source without changing the indexing/chunking policy. */
|
|
659
|
+
export async function withSyntaxTree<T>(
|
|
660
|
+
source: string,
|
|
661
|
+
lang: string,
|
|
662
|
+
inspect: (root: Node) => T,
|
|
663
|
+
): Promise<T> {
|
|
664
|
+
const grammar = await language(lang);
|
|
665
|
+
const parser = new Parser();
|
|
666
|
+
let tree: ReturnType<Parser["parse"]> = null;
|
|
667
|
+
try {
|
|
668
|
+
parser.setLanguage(grammar);
|
|
669
|
+
tree = parser.parse(source);
|
|
670
|
+
invariant(tree, "Parser returned no tree.");
|
|
671
|
+
return inspect(tree.rootNode);
|
|
672
|
+
} finally {
|
|
673
|
+
tree?.delete();
|
|
674
|
+
parser.delete();
|
|
675
|
+
}
|
|
676
|
+
}
|