@mrclrchtr/supi-tree-sitter 3.2.0 → 4.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.
Files changed (55) hide show
  1. package/README.md +4 -0
  2. package/node_modules/@mrclrchtr/supi-code-runtime/README.md +8 -0
  3. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +2 -4
  4. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/api.ts +0 -4
  5. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/evidence-badge.ts +3 -2
  6. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/index.ts +0 -4
  7. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/llm.ts +0 -10
  8. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/path-utils.ts +19 -15
  9. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +2 -2
  10. package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +10 -1
  11. package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +19 -29
  12. package/node_modules/@mrclrchtr/supi-code-runtime/src/index.ts +7 -1
  13. package/node_modules/@mrclrchtr/supi-code-runtime/src/query-result.ts +42 -0
  14. package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +15 -10
  15. package/node_modules/@mrclrchtr/supi-core/package.json +2 -4
  16. package/node_modules/@mrclrchtr/supi-core/src/api.ts +0 -4
  17. package/node_modules/@mrclrchtr/supi-core/src/evidence-badge.ts +3 -2
  18. package/node_modules/@mrclrchtr/supi-core/src/index.ts +0 -4
  19. package/node_modules/@mrclrchtr/supi-core/src/llm.ts +0 -10
  20. package/node_modules/@mrclrchtr/supi-core/src/path-utils.ts +19 -15
  21. package/package.json +4 -21
  22. package/resources/grammars/bash/tree-sitter-bash.wasm +0 -0
  23. package/resources/grammars/bash/tree-sitter-bash.wasm.json +2 -2
  24. package/resources/grammars/c/tree-sitter-c.wasm +0 -0
  25. package/resources/grammars/c/tree-sitter-c.wasm.json +2 -2
  26. package/resources/grammars/go/tree-sitter-go.wasm +0 -0
  27. package/resources/grammars/go/tree-sitter-go.wasm.json +2 -2
  28. package/resources/grammars/javascript/tree-sitter-javascript.wasm +0 -0
  29. package/resources/grammars/javascript/tree-sitter-javascript.wasm.json +2 -2
  30. package/resources/grammars/kotlin/tree-sitter-kotlin.wasm +0 -0
  31. package/resources/grammars/kotlin/tree-sitter-kotlin.wasm.json +2 -2
  32. package/resources/grammars/python/tree-sitter-python.wasm +0 -0
  33. package/resources/grammars/python/tree-sitter-python.wasm.json +2 -2
  34. package/resources/grammars/r/tree-sitter-r.wasm +0 -0
  35. package/resources/grammars/r/tree-sitter-r.wasm.json +2 -2
  36. package/resources/grammars/rust/tree-sitter-rust.wasm.json +1 -1
  37. package/resources/grammars/sql/tree-sitter-sql.wasm +0 -0
  38. package/resources/grammars/sql/tree-sitter-sql.wasm.json +3 -3
  39. package/scripts/generate-kotlin-wasm.mjs +6 -0
  40. package/scripts/generate-sql-wasm.mjs +18 -4
  41. package/scripts/vendor-wasm.mjs +17 -1
  42. package/src/api.ts +2 -0
  43. package/src/coordinates.ts +20 -41
  44. package/src/index.ts +2 -0
  45. package/src/language.ts +7 -0
  46. package/src/operation-support.ts +40 -0
  47. package/src/session/session.ts +9 -7
  48. package/src/tool/call-sites.ts +6 -1
  49. package/src/tool/callees.ts +76 -37
  50. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +0 -31
  51. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/substrate-types.ts +0 -11
  52. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/types.ts +0 -2
  53. package/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +0 -31
  54. package/node_modules/@mrclrchtr/supi-core/src/substrate-types.ts +0 -11
  55. package/node_modules/@mrclrchtr/supi-core/src/types.ts +0 -2
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "source": {
3
3
  "npmPackage": "tree-sitter-bash",
4
- "version": "0.23.3"
4
+ "version": "0.25.1"
5
5
  },
6
- "sha256": "d1844429a58620f306b6f42aebe92298243ca8120cd833a3ab5d87c7a2e7b9fd"
6
+ "sha256": "8292919c88a0f7d3fb31d0cd0253ca5a9531bc1ede82b0537f2c63dd8abe6a7a"
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "source": {
3
3
  "npmPackage": "tree-sitter-c",
4
- "version": "0.23.6"
4
+ "version": "0.24.1"
5
5
  },
6
- "sha256": "146f85977800935f18b06940518b16ded13cf4007ef0e3190573b969a98b9adc"
6
+ "sha256": "c852c2a85ebf2beb636aa3b0ef7f7e70458684d74f6741b20dcb296885bed9f9"
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "source": {
3
3
  "npmPackage": "tree-sitter-go",
4
- "version": "0.23.4"
4
+ "version": "0.25.0"
5
5
  },
6
- "sha256": "6dfc8eacdad0a54d0cad0d888851bd19cdd14d82582f110f888bbf6f9e5e2d64"
6
+ "sha256": "9504573f352b20be7f2f1911754d710622aedc15afff16d5ed8fb5645681aee7"
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "source": {
3
3
  "npmPackage": "tree-sitter-javascript",
4
- "version": "0.23.1"
4
+ "version": "0.25.0"
5
5
  },
6
- "sha256": "4a378293fe7853cbee2836023be072dafa0e53b3b5edb245920838ca834ed121"
6
+ "sha256": "5fb488d0cabb4775a594bab85682de5ad6ce83c0d6ac997a9f82dd084d571240"
7
7
  }
@@ -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.22.6"
9
+ "treeSitterCli": "0.26.11"
10
10
  },
11
- "sha256": "c624e7443b371c28adc5d81674e73067564c12555ebe3ed96a6c8db814b7602d"
11
+ "sha256": "e08447e36ad5d86f39f91f13defce0636aea1490b8d65dafde6fa72649422061"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "source": {
3
3
  "npmPackage": "tree-sitter-python",
4
- "version": "0.23.6"
4
+ "version": "0.25.0"
5
5
  },
6
- "sha256": "8c93692fb368e288a5824cee55773c9b3602804f513bda48c97661e52e9c2da2"
6
+ "sha256": "16108b50df4ee9a30168794252ab55e7c93bfc5765d7fa0aa3e335752c515f47"
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "source": {
3
3
  "npmPackage": "@davisvaughan/tree-sitter-r",
4
- "version": "1.2.0"
4
+ "version": "1.3.0"
5
5
  },
6
- "sha256": "2a8f5acd1c53d91e0ec5c01a6830d8ac7f5a7f96f0ac4b3768c016c8e9d07711"
6
+ "sha256": "a39d2e71bd39c29bb7f144a03f41d6d9f0099fede4110c696dc0715a62c9fbe1"
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "source": {
3
3
  "npmPackage": "tree-sitter-rust",
4
- "version": "0.23.3"
4
+ "version": "0.24.0"
5
5
  },
6
6
  "sha256": "f65f354215611fd94ad34134b3427eb3d58cbb745df7b6509ba722184db73d57"
7
7
  }
@@ -5,11 +5,11 @@
5
5
  "repository": "https://github.com/derekstride/tree-sitter-sql"
6
6
  },
7
7
  "generatedWith": {
8
- "treeSitterCli": "0.22.6"
8
+ "treeSitterCli": "0.26.11"
9
9
  },
10
- "sha256": "0871a3bd488ed39f5a521484337f07a43bd6aa95dbdf1cdd24744deecafa58fc",
10
+ "sha256": "9b93a15dc3eade66df4347731f2687b939daf987c8382660d6118e863604ab09",
11
11
  "trust": {
12
- "note": "The @derekstride/tree-sitter-sql npm package is a devDependency only. It is never resolved at runtime — the vendored WASM above is the sole runtime artifact. The npm package's install script uses 'npx --yes' which some package managers flag as a supply-chain risk; pnpm ignores build scripts by default, so this script never runs during install. The WASM was built locally from the package source (not downloaded from npm). See scripts/generate-sql-wasm.mjs for the rebuild procedure.",
12
+ "note": "The @derekstride/tree-sitter-sql npm package is a devDependency only. It is never resolved at runtime; the vendored WASM above is the sole runtime artifact. This workspace explicitly disables the package's npx-based install script via pnpm allowBuilds. The WASM was built locally from the installed package source. See scripts/generate-sql-wasm.mjs for the rebuild procedure.",
13
13
  "alternativesConsidered": [
14
14
  "tree-sitter-sql (m-novikov, 2021, stale)",
15
15
  "tree-sitter-sql-bigquery (BigQuery-specific)",
@@ -38,6 +38,7 @@ function expectedReleaseAsset(version) {
38
38
 
39
39
  function assertKotlinWasmCurrent() {
40
40
  const kotlinPackage = readPackage("tree-sitter-kotlin");
41
+ const cliPackage = readPackage("tree-sitter-cli");
41
42
  const metadata = readMetadata();
42
43
  const actualSha = sha256(wasmPath);
43
44
  const errors = [];
@@ -55,6 +56,11 @@ function assertKotlinWasmCurrent() {
55
56
  "metadata release asset URL does not match the installed tree-sitter-kotlin version",
56
57
  );
57
58
  }
59
+ if (metadata.generatedWith?.treeSitterCli !== cliPackage.json.version) {
60
+ errors.push(
61
+ `metadata pins tree-sitter-cli ${metadata.generatedWith?.treeSitterCli}, but installed CLI is ${cliPackage.json.version}`,
62
+ );
63
+ }
58
64
  if (metadata.sha256 !== actualSha) {
59
65
  errors.push(`metadata sha256 ${metadata.sha256} does not match vendored file ${actualSha}`);
60
66
  }
@@ -8,10 +8,10 @@
8
8
  *
9
9
  * Trust considerations:
10
10
  * - The @derekstride/tree-sitter-sql install script uses "npx --yes", which
11
- * some package managers flag as a supply-chain risk. pnpm ignores build
12
- * scripts by default, so this script never runs during install.
11
+ * some package managers flag as a supply-chain risk. This workspace disables
12
+ * that package's build script explicitly via pnpm `allowBuilds`.
13
13
  * - The WASM is built locally from the installed package source (not downloaded
14
- * from npm). The build uses tree-sitter-cli 0.22.6 with Emscripten/Docker.
14
+ * from npm). The build uses tree-sitter-cli with Emscripten/Docker.
15
15
  * - Alternatives considered: tree-sitter-sql (m-novikov, stale since 2021),
16
16
  * tree-sitter-sql-bigquery (dialect-specific), and dialect-specific grammars.
17
17
  * derekstride/tree-sitter-sql is the most mature general-purpose SQL grammar.
@@ -55,6 +55,7 @@ function readMetadata() {
55
55
 
56
56
  function assertSqlWasmCurrent() {
57
57
  const sqlPackage = readPackage("@derekstride/tree-sitter-sql");
58
+ const cliPackage = readPackage("tree-sitter-cli");
58
59
  const metadata = readMetadata();
59
60
  const actualSha = sha256(wasmPath);
60
61
  const errors = [];
@@ -67,6 +68,11 @@ function assertSqlWasmCurrent() {
67
68
  `metadata pins @derekstride/tree-sitter-sql ${metadata.source?.version}, but installed package is ${sqlPackage.json.version}`,
68
69
  );
69
70
  }
71
+ if (metadata.generatedWith?.treeSitterCli !== cliPackage.json.version) {
72
+ errors.push(
73
+ `metadata pins tree-sitter-cli ${metadata.generatedWith?.treeSitterCli}, but installed CLI is ${cliPackage.json.version}`,
74
+ );
75
+ }
70
76
  if (metadata.sha256 !== actualSha) {
71
77
  errors.push(`metadata sha256 ${metadata.sha256} does not match vendored file ${actualSha}`);
72
78
  }
@@ -92,7 +98,7 @@ function generateSqlWasm() {
92
98
  try {
93
99
  fs.cpSync(sqlPackage.dir, grammarDir, { recursive: true });
94
100
 
95
- // tree-sitter-cli 0.22.6 requires a "tree-sitter" section in package.json
101
+ // tree-sitter-cli requires a "tree-sitter" section in package.json
96
102
  const grammarPackageJsonPath = path.join(grammarDir, "package.json");
97
103
  const grammarPackageJson = JSON.parse(fs.readFileSync(grammarPackageJsonPath, "utf-8"));
98
104
  grammarPackageJson["tree-sitter"] = [{ scope: "source.sql" }];
@@ -128,6 +134,14 @@ function generateSqlWasm() {
128
134
  treeSitterCli: cliPackage.json.version,
129
135
  },
130
136
  sha256: checksum,
137
+ trust: {
138
+ note: "The @derekstride/tree-sitter-sql npm package is a devDependency only. It is never resolved at runtime; the vendored WASM above is the sole runtime artifact. This workspace explicitly disables the package's npx-based install script via pnpm allowBuilds. The WASM was built locally from the installed package source. See scripts/generate-sql-wasm.mjs for the rebuild procedure.",
139
+ alternativesConsidered: [
140
+ "tree-sitter-sql (m-novikov, 2021, stale)",
141
+ "tree-sitter-sql-bigquery (BigQuery-specific)",
142
+ "dialect-specific grammars (too narrow)",
143
+ ],
144
+ },
131
145
  };
132
146
  fs.writeFileSync(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`);
133
147
  process.stdout.write(`Generated ${wasmPath}\n`);
@@ -108,8 +108,14 @@ function checkGrammar(grammarId, source) {
108
108
 
109
109
  try {
110
110
  const pkg = readPackage(source.npmPackage);
111
+ const sourceWasmPath = path.join(pkg.dir, source.wasmFile);
112
+ if (!fs.existsSync(sourceWasmPath)) {
113
+ errors.push(`${grammarId}: installed package is missing ${source.wasmFile}`);
114
+ return errors;
115
+ }
111
116
  const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
112
117
  const actualSha = sha256(wasmPath);
118
+ const sourceSha = sha256(sourceWasmPath);
113
119
 
114
120
  if (metadata.source?.npmPackage !== source.npmPackage) {
115
121
  errors.push(`${grammarId}: metadata source npmPackage mismatch`);
@@ -119,8 +125,18 @@ function checkGrammar(grammarId, source) {
119
125
  `${grammarId}: metadata version ${metadata.source?.version} !== installed ${pkg.json.version}`,
120
126
  );
121
127
  }
128
+ if (actualSha !== sourceSha) {
129
+ errors.push(
130
+ `${grammarId}: vendored sha256 ${actualSha} !== installed package source ${sourceSha}`,
131
+ );
132
+ }
122
133
  if (metadata.sha256 !== actualSha) {
123
- errors.push(`${grammarId}: metadata sha256 ${metadata.sha256} !== actual ${actualSha}`);
134
+ errors.push(`${grammarId}: metadata sha256 ${metadata.sha256} !== vendored ${actualSha}`);
135
+ }
136
+ if (metadata.sha256 !== sourceSha) {
137
+ errors.push(
138
+ `${grammarId}: metadata sha256 ${metadata.sha256} !== installed package source ${sourceSha}`,
139
+ );
124
140
  }
125
141
  } catch (err) {
126
142
  errors.push(`${grammarId}: ${err.message}`);
package/src/api.ts CHANGED
@@ -12,6 +12,8 @@ export {
12
12
  isJsTsGrammar,
13
13
  isSupportedFile,
14
14
  } from "./language.ts";
15
+ export type { StructuralSearchOperation } from "./operation-support.ts";
16
+ export { getStructuralSearchSupportedExtensions } from "./operation-support.ts";
15
17
  export { TreeSitterRuntime } from "./session/runtime.ts";
16
18
  export type {
17
19
  TsControllerState,
@@ -1,10 +1,16 @@
1
1
  // Coordinate conversion between public 1-based UTF-16 positions
2
- // and Tree-sitter runtime byte/row/column positions.
2
+ // and Tree-sitter runtime positions.
3
+ //
4
+ // web-tree-sitter's Parser.parse(string) uses JavaScript string columns
5
+ // (UTF-16 code unit offsets) for startPosition/endPosition. We convert
6
+ // 1-based user-facing positions to 0-based row/column and back without
7
+ // any byte-level encoding.
8
+ //
9
+ // This matches the column convention used by LSP and Monaco, so ranges
10
+ // are directly shareable between tree-sitter and LSP workflows.
3
11
 
4
12
  import type { SourceRange } from "./types.ts";
5
13
 
6
- const encoder = new TextEncoder();
7
-
8
14
  /** Point in source: 1-based line and UTF-16 character. */
9
15
  export interface PublicPoint {
10
16
  line: number;
@@ -16,7 +22,8 @@ export interface PublicPoint {
16
22
  * for Tree-sitter lookup.
17
23
  *
18
24
  * `character` is a UTF-16 code-unit column (editor/LSP convention).
19
- * We convert it to a byte offset within the line by counting UTF-8 bytes.
25
+ * The column maps directly to JavaScript string code units — no byte
26
+ * conversion is needed for web-tree-sitter's string-parsing mode.
20
27
  */
21
28
  export function publicToTreeSitter(
22
29
  line: number,
@@ -25,32 +32,27 @@ export function publicToTreeSitter(
25
32
  ): { row: number; column: number } {
26
33
  const row = line - 1;
27
34
  const lines = splitSourceLines(source);
28
- const lineText = row < lines.length ? lines[row] : "";
29
- // character is 1-based; clamp to line length
30
- const charIndex = Math.max(0, Math.min(character - 1, lineText.length));
31
- // Convert UTF-16 code unit index to byte offset
32
- const textBefore = lineText.substring(0, charIndex);
33
- const byteOffset = encoder.encode(textBefore).length;
34
- return { row, column: byteOffset };
35
+ const lineText = row < lines.length ? (lines[row] ?? "") : "";
36
+ // Clamp column to the line length.
37
+ const column = Math.max(0, Math.min(character - 1, lineText.length));
38
+ return { row, column };
35
39
  }
36
40
 
37
41
  /**
38
42
  * Convert a Tree-sitter (row, column) pair to a 1-based (line, character) pair.
39
43
  *
40
- * `column` is a byte offset within the line; we convert it to a UTF-16
41
- * code-unit index.
44
+ * `column` is a zero-based JavaScript string code unit offset within the line.
42
45
  */
43
46
  export function treeSitterToPublic(row: number, column: number, source: string): PublicPoint {
47
+ // Clamp to EOL for positions right after the last character.
44
48
  const lines = splitSourceLines(source);
45
- const lineText = row < lines.length ? lines[row] : "";
46
- // Convert byte offset to UTF-16 code unit index
47
- const charIndex = byteToUtf16Index(lineText, column);
48
- return { line: row + 1, character: charIndex + 1 };
49
+ const lineText = row < lines.length ? (lines[row] ?? "") : "";
50
+ const safeColumn = Math.max(0, Math.min(column, lineText.length));
51
+ return { line: row + 1, character: safeColumn + 1 };
49
52
  }
50
53
 
51
54
  /**
52
55
  * Convert a Tree-sitter point {row, column} to a SourceRange-compatible point.
53
- * Uses the source text for byte-to-UTF16 conversion.
54
56
  */
55
57
  export function tsPointToPublic(
56
58
  point: { row: number; column: number },
@@ -83,26 +85,3 @@ export function nodeToRange(
83
85
  export function splitSourceLines(source: string): string[] {
84
86
  return source.replace(/\r\n?/g, "\n").split("\n");
85
87
  }
86
-
87
- /**
88
- * Convert a byte offset within a line to a UTF-16 code unit index.
89
- */
90
- function byteToUtf16Index(line: string, byteOffset: number): number {
91
- let byteCount = 0;
92
- for (let i = 0; i < line.length; i++) {
93
- if (byteCount >= byteOffset) return i;
94
- const char = line.charCodeAt(i);
95
- // surrogate pair — 4 bytes in UTF-8, but 2 UTF-16 code units
96
- if (char >= 0xd800 && char <= 0xdbff) {
97
- byteCount += 4;
98
- i++; // skip low surrogate
99
- } else if (char > 0x7f) {
100
- // Non-ASCII: 2-3 bytes in UTF-8
101
- if (char <= 0x7ff) byteCount += 2;
102
- else byteCount += 3;
103
- } else {
104
- byteCount += 1;
105
- }
106
- }
107
- return line.length;
108
- }
package/src/index.ts CHANGED
@@ -11,6 +11,8 @@ export {
11
11
  isJsTsGrammar,
12
12
  isSupportedFile,
13
13
  } from "./language.ts";
14
+ export type { StructuralSearchOperation } from "./operation-support.ts";
15
+ export { getStructuralSearchSupportedExtensions } from "./operation-support.ts";
14
16
  // Provider implementation
15
17
  export { createTreeSitterProvider } from "./provider/tree-sitter-provider.ts";
16
18
  export { TreeSitterRuntime } from "./session/runtime.ts";
package/src/language.ts CHANGED
@@ -61,6 +61,13 @@ export function getSupportedExtensions(): string[] {
61
61
  return [...SUPPORTED_EXTENSIONS];
62
62
  }
63
63
 
64
+ /** Internal registry snapshot used to derive operation-specific language support. */
65
+ export function getSupportedExtensionGrammarEntries(): Array<
66
+ readonly [SupportedExtension, GrammarId]
67
+ > {
68
+ return Object.entries(EXTENSION_GRAMMAR) as Array<readonly [SupportedExtension, GrammarId]>;
69
+ }
70
+
64
71
  /** Get the file extension if it's supported, otherwise undefined. */
65
72
  export function getSupportedExtension(filePath: string): SupportedExtension | undefined {
66
73
  const ext = path.extname(filePath).toLowerCase();
@@ -0,0 +1,40 @@
1
+ import { getSupportedExtensionGrammarEntries, isJsTsGrammar } from "./language.ts";
2
+ import { supportsCallSitesGrammar } from "./tool/call-sites.ts";
3
+ import type { GrammarId, SupportedExtension } from "./types.ts";
4
+
5
+ /** Structural collection operations whose language support defines an AST Scan universe. */
6
+ const STRUCTURAL_SEARCH_OPERATIONS = ["outline", "imports", "exports", "call-sites"] as const;
7
+
8
+ /** One Tree-sitter structural collection operation used by code-aware search. */
9
+ export type StructuralSearchOperation = (typeof STRUCTURAL_SEARCH_OPERATIONS)[number];
10
+
11
+ /**
12
+ * Return the file extensions that the concrete extractor supports for an operation.
13
+ *
14
+ * This is operation support, not general parser support. The result is derived from
15
+ * the same grammar predicates and query registry used by the extractors themselves.
16
+ */
17
+ export function getStructuralSearchSupportedExtensions(
18
+ operation: StructuralSearchOperation,
19
+ ): SupportedExtension[] {
20
+ return getSupportedExtensionGrammarEntries()
21
+ .filter(([, grammar]) => supportsGrammarOperation(grammar, operation))
22
+ .map(([extension]) => extension);
23
+ }
24
+
25
+ /** Return whether one grammar is supported by the requested structural operation. */
26
+ export function supportsGrammarOperation(
27
+ grammar: GrammarId,
28
+ operation: StructuralSearchOperation,
29
+ ): boolean {
30
+ switch (operation) {
31
+ case "call-sites":
32
+ return supportsCallSitesGrammar(grammar);
33
+ case "outline":
34
+ case "imports":
35
+ case "exports":
36
+ return isJsTsGrammar(grammar);
37
+ default:
38
+ throw new TypeError(`Unsupported structural search operation: ${String(operation)}`);
39
+ }
40
+ }
@@ -1,6 +1,7 @@
1
1
  // Session factory — creates runtime-backed Tree-sitter services and owned sessions.
2
2
 
3
- import { detectGrammar, isJsTsGrammar } from "../language.ts";
3
+ import { detectGrammar } from "../language.ts";
4
+ import { supportsGrammarOperation } from "../operation-support.ts";
4
5
  import {
5
6
  extractCallSites,
6
7
  extractExports,
@@ -45,15 +46,16 @@ export function createTreeSitterService(runtime: TreeSitterRuntime): TreeSitterS
45
46
  },
46
47
 
47
48
  async outline(file: string): Promise<TreeSitterResult<OutlineItem[]>> {
48
- const parseResult = await runtime.parseFile(file);
49
- if (parseResult.kind !== "success") return parseResult;
50
- if (!isJsTsGrammar(parseResult.data.grammarId)) {
49
+ const grammarId = detectGrammar(file);
50
+ if (grammarId && !supportsGrammarOperation(grammarId, "outline")) {
51
51
  return {
52
52
  kind: "unsupported-language",
53
53
  file,
54
- message: `outline is not supported for ${parseResult.data.grammarId} files`,
54
+ message: `outline is not supported for ${grammarId} files`,
55
55
  };
56
56
  }
57
+ const parseResult = await runtime.parseFile(file);
58
+ if (parseResult.kind !== "success") return parseResult;
57
59
  const { tree, source } = parseResult.data;
58
60
  try {
59
61
  const items = extractOutline(tree.rootNode, source);
@@ -65,7 +67,7 @@ export function createTreeSitterService(runtime: TreeSitterRuntime): TreeSitterS
65
67
 
66
68
  async imports(file: string): Promise<TreeSitterResult<ImportRecord[]>> {
67
69
  const grammarId = detectGrammar(file);
68
- if (grammarId && !isJsTsGrammar(grammarId)) {
70
+ if (grammarId && !supportsGrammarOperation(grammarId, "imports")) {
69
71
  return {
70
72
  kind: "unsupported-language",
71
73
  file,
@@ -77,7 +79,7 @@ export function createTreeSitterService(runtime: TreeSitterRuntime): TreeSitterS
77
79
 
78
80
  async exports(file: string): Promise<TreeSitterResult<ExportRecord[]>> {
79
81
  const grammarId = detectGrammar(file);
80
- if (grammarId && !isJsTsGrammar(grammarId)) {
82
+ if (grammarId && !supportsGrammarOperation(grammarId, "exports")) {
81
83
  return {
82
84
  kind: "unsupported-language",
83
85
  file,
@@ -30,10 +30,15 @@ const CALL_SITE_QUERIES: Partial<Record<GrammarId, string>> = {
30
30
  kotlin:
31
31
  "(call_expression . (simple_identifier) @call) (call_expression . (navigation_expression . (simple_identifier) @call))",
32
32
  ruby: "(call method: (identifier) @call)",
33
- bash: "(command name: (word) @call)",
33
+ bash: "(command name: (command_name) @call)",
34
34
  r: "(call function: (identifier) @call)",
35
35
  };
36
36
 
37
+ /** Return whether the call-site extractor has a query for one grammar. */
38
+ export function supportsCallSitesGrammar(grammarId: GrammarId): boolean {
39
+ return CALL_SITE_QUERIES[grammarId] !== undefined;
40
+ }
41
+
37
42
  /**
38
43
  * Extract all call-site callee expressions from a file.
39
44
  *