kiri-mcp-server 0.16.1 → 0.18.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 (164) hide show
  1. package/README.md +65 -22
  2. package/dist/package.json +4 -2
  3. package/dist/src/client/proxy.js +0 -0
  4. package/dist/src/daemon/daemon.js +0 -0
  5. package/dist/src/indexer/codeintel/dart/adapter.d.ts +36 -0
  6. package/dist/src/indexer/codeintel/dart/adapter.d.ts.map +1 -0
  7. package/dist/src/indexer/codeintel/dart/adapter.js +60 -0
  8. package/dist/src/indexer/codeintel/dart/adapter.js.map +1 -0
  9. package/dist/src/indexer/codeintel/dart/index.d.ts +7 -0
  10. package/dist/src/indexer/codeintel/dart/index.d.ts.map +1 -0
  11. package/dist/src/indexer/codeintel/dart/index.js +7 -0
  12. package/dist/src/indexer/codeintel/dart/index.js.map +1 -0
  13. package/dist/src/indexer/codeintel/index.d.ts +30 -0
  14. package/dist/src/indexer/codeintel/index.d.ts.map +1 -0
  15. package/dist/src/indexer/codeintel/index.js +32 -0
  16. package/dist/src/indexer/codeintel/index.js.map +1 -0
  17. package/dist/src/indexer/codeintel/java/analyzer.d.ts +22 -0
  18. package/dist/src/indexer/codeintel/java/analyzer.d.ts.map +1 -0
  19. package/dist/src/indexer/codeintel/java/analyzer.js +281 -0
  20. package/dist/src/indexer/codeintel/java/analyzer.js.map +1 -0
  21. package/dist/src/indexer/codeintel/java/index.d.ts +7 -0
  22. package/dist/src/indexer/codeintel/java/index.d.ts.map +1 -0
  23. package/dist/src/indexer/codeintel/java/index.js +7 -0
  24. package/dist/src/indexer/codeintel/java/index.js.map +1 -0
  25. package/dist/src/indexer/codeintel/php/analyzer.d.ts +23 -0
  26. package/dist/src/indexer/codeintel/php/analyzer.d.ts.map +1 -0
  27. package/dist/src/indexer/codeintel/php/analyzer.js +342 -0
  28. package/dist/src/indexer/codeintel/php/analyzer.js.map +1 -0
  29. package/dist/src/indexer/codeintel/php/index.d.ts +7 -0
  30. package/dist/src/indexer/codeintel/php/index.d.ts.map +1 -0
  31. package/dist/src/indexer/codeintel/php/index.js +7 -0
  32. package/dist/src/indexer/codeintel/php/index.js.map +1 -0
  33. package/dist/src/indexer/codeintel/registry.d.ts +76 -0
  34. package/dist/src/indexer/codeintel/registry.d.ts.map +1 -0
  35. package/dist/src/indexer/codeintel/registry.js +127 -0
  36. package/dist/src/indexer/codeintel/registry.js.map +1 -0
  37. package/dist/src/indexer/codeintel/rust/analyzer.d.ts +14 -0
  38. package/dist/src/indexer/codeintel/rust/analyzer.d.ts.map +1 -0
  39. package/dist/src/indexer/codeintel/rust/analyzer.js +388 -0
  40. package/dist/src/indexer/codeintel/rust/analyzer.js.map +1 -0
  41. package/dist/src/indexer/codeintel/rust/index.d.ts +5 -0
  42. package/dist/src/indexer/codeintel/rust/index.d.ts.map +1 -0
  43. package/dist/src/indexer/codeintel/rust/index.js +5 -0
  44. package/dist/src/indexer/codeintel/rust/index.js.map +1 -0
  45. package/dist/src/indexer/codeintel/swift/analyzer.d.ts +22 -0
  46. package/dist/src/indexer/codeintel/swift/analyzer.d.ts.map +1 -0
  47. package/dist/src/indexer/codeintel/swift/analyzer.js +271 -0
  48. package/dist/src/indexer/codeintel/swift/analyzer.js.map +1 -0
  49. package/dist/src/indexer/codeintel/swift/index.d.ts +7 -0
  50. package/dist/src/indexer/codeintel/swift/index.d.ts.map +1 -0
  51. package/dist/src/indexer/codeintel/swift/index.js +7 -0
  52. package/dist/src/indexer/codeintel/swift/index.js.map +1 -0
  53. package/dist/src/indexer/codeintel/types.d.ts +114 -0
  54. package/dist/src/indexer/codeintel/types.d.ts.map +1 -0
  55. package/dist/src/indexer/codeintel/types.js +13 -0
  56. package/dist/src/indexer/codeintel/types.js.map +1 -0
  57. package/dist/src/indexer/codeintel/typescript/analyzer.d.ts +22 -0
  58. package/dist/src/indexer/codeintel/typescript/analyzer.d.ts.map +1 -0
  59. package/dist/{indexer/codeintel.js → src/indexer/codeintel/typescript/analyzer.js} +62 -34
  60. package/dist/src/indexer/codeintel/typescript/analyzer.js.map +1 -0
  61. package/dist/src/indexer/codeintel/typescript/index.d.ts +7 -0
  62. package/dist/src/indexer/codeintel/typescript/index.d.ts.map +1 -0
  63. package/dist/src/indexer/codeintel/typescript/index.js +7 -0
  64. package/dist/src/indexer/codeintel/typescript/index.js.map +1 -0
  65. package/dist/src/indexer/codeintel/utils.d.ts +91 -0
  66. package/dist/src/indexer/codeintel/utils.d.ts.map +1 -0
  67. package/dist/src/indexer/codeintel/utils.js +145 -0
  68. package/dist/src/indexer/codeintel/utils.js.map +1 -0
  69. package/dist/src/indexer/codeintel.d.ts +33 -26
  70. package/dist/src/indexer/codeintel.d.ts.map +1 -1
  71. package/dist/src/indexer/codeintel.js +56 -1078
  72. package/dist/src/indexer/codeintel.js.map +1 -1
  73. package/dist/src/indexer/graph-metrics.d.ts.map +1 -1
  74. package/dist/src/indexer/graph-metrics.js +16 -4
  75. package/dist/src/indexer/graph-metrics.js.map +1 -1
  76. package/dist/src/server/boost-profiles.d.ts +1 -1
  77. package/dist/src/server/boost-profiles.d.ts.map +1 -1
  78. package/dist/src/server/boost-profiles.js +22 -0
  79. package/dist/src/server/boost-profiles.js.map +1 -1
  80. package/dist/src/server/main.js +0 -0
  81. package/dist/src/server/rpc.js +4 -4
  82. package/dist/src/server/rpc.js.map +1 -1
  83. package/package.json +10 -2
  84. package/dist/client/cli.js +0 -68
  85. package/dist/client/cli.js.map +0 -1
  86. package/dist/client/index.js +0 -5
  87. package/dist/client/index.js.map +0 -1
  88. package/dist/eval/metrics.js +0 -47
  89. package/dist/eval/metrics.js.map +0 -1
  90. package/dist/indexer/cli.js +0 -362
  91. package/dist/indexer/cli.js.map +0 -1
  92. package/dist/indexer/codeintel.js.map +0 -1
  93. package/dist/indexer/git.js +0 -30
  94. package/dist/indexer/git.js.map +0 -1
  95. package/dist/indexer/language.js +0 -34
  96. package/dist/indexer/language.js.map +0 -1
  97. package/dist/indexer/pipeline/filters/denylist.js +0 -71
  98. package/dist/indexer/pipeline/filters/denylist.js.map +0 -1
  99. package/dist/indexer/schema.js +0 -101
  100. package/dist/indexer/schema.js.map +0 -1
  101. package/dist/server/bootstrap.js +0 -19
  102. package/dist/server/bootstrap.js.map +0 -1
  103. package/dist/server/context.js +0 -1
  104. package/dist/server/context.js.map +0 -1
  105. package/dist/server/fallbacks/degradeController.js +0 -69
  106. package/dist/server/fallbacks/degradeController.js.map +0 -1
  107. package/dist/server/handlers.js +0 -1268
  108. package/dist/server/handlers.js.map +0 -1
  109. package/dist/server/main.js +0 -151
  110. package/dist/server/main.js.map +0 -1
  111. package/dist/server/observability/metrics.js +0 -56
  112. package/dist/server/observability/metrics.js.map +0 -1
  113. package/dist/server/observability/tracing.js +0 -58
  114. package/dist/server/observability/tracing.js.map +0 -1
  115. package/dist/server/rpc.js +0 -477
  116. package/dist/server/rpc.js.map +0 -1
  117. package/dist/server/runtime.js +0 -47
  118. package/dist/server/runtime.js.map +0 -1
  119. package/dist/server/scoring.js +0 -116
  120. package/dist/server/scoring.js.map +0 -1
  121. package/dist/server/stdio.js +0 -76
  122. package/dist/server/stdio.js.map +0 -1
  123. package/dist/shared/duckdb.js +0 -119
  124. package/dist/shared/duckdb.js.map +0 -1
  125. package/dist/shared/embedding.js +0 -98
  126. package/dist/shared/embedding.js.map +0 -1
  127. package/dist/shared/index.js +0 -9
  128. package/dist/shared/index.js.map +0 -1
  129. package/dist/shared/security/config.js +0 -64
  130. package/dist/shared/security/config.js.map +0 -1
  131. package/dist/shared/security/masker.js +0 -56
  132. package/dist/shared/security/masker.js.map +0 -1
  133. package/dist/shared/tokenizer.js +0 -4
  134. package/dist/shared/tokenizer.js.map +0 -1
  135. package/dist/shared/utils/simpleYaml.js +0 -89
  136. package/dist/shared/utils/simpleYaml.js.map +0 -1
  137. package/dist/src/server/rrf.d.ts +0 -86
  138. package/dist/src/server/rrf.d.ts.map +0 -1
  139. package/dist/src/server/rrf.js +0 -108
  140. package/dist/src/server/rrf.js.map +0 -1
  141. package/dist/src/shared/embedding/engine.d.ts +0 -38
  142. package/dist/src/shared/embedding/engine.d.ts.map +0 -1
  143. package/dist/src/shared/embedding/engine.js +0 -6
  144. package/dist/src/shared/embedding/engine.js.map +0 -1
  145. package/dist/src/shared/embedding/lsh-engine.d.ts +0 -11
  146. package/dist/src/shared/embedding/lsh-engine.d.ts.map +0 -1
  147. package/dist/src/shared/embedding/lsh-engine.js +0 -14
  148. package/dist/src/shared/embedding/lsh-engine.js.map +0 -1
  149. package/dist/src/shared/embedding/registry.d.ts +0 -25
  150. package/dist/src/shared/embedding/registry.d.ts.map +0 -1
  151. package/dist/src/shared/embedding/registry.js +0 -50
  152. package/dist/src/shared/embedding/registry.js.map +0 -1
  153. package/dist/src/shared/embedding/semantic-engine.d.ts +0 -14
  154. package/dist/src/shared/embedding/semantic-engine.d.ts.map +0 -1
  155. package/dist/src/shared/embedding/semantic-engine.js +0 -50
  156. package/dist/src/shared/embedding/semantic-engine.js.map +0 -1
  157. package/dist/src/shared/models/model-manager.d.ts +0 -38
  158. package/dist/src/shared/models/model-manager.d.ts.map +0 -1
  159. package/dist/src/shared/models/model-manager.js +0 -116
  160. package/dist/src/shared/models/model-manager.js.map +0 -1
  161. package/dist/src/shared/models/model-manifest.d.ts +0 -22
  162. package/dist/src/shared/models/model-manifest.d.ts.map +0 -1
  163. package/dist/src/shared/models/model-manifest.js +0 -24
  164. package/dist/src/shared/models/model-manifest.js.map +0 -1
@@ -1,1088 +1,66 @@
1
- import { createRequire } from "node:module";
2
- import path from "node:path";
3
- import Parser from "tree-sitter";
4
- import Java from "tree-sitter-java";
5
- import Swift from "tree-sitter-swift";
6
- import ts from "typescript";
7
- // tree-sitter-php is a CommonJS module, so import it using require.
8
- // tree-sitter-php: Using version 0.22.8 for compatibility with tree-sitter 0.22.4.
9
- // Version 0.24.2 had a nodeTypeInfo bug that caused runtime errors.
10
- // php_only: for pure PHP files (<?php at start)
11
- // php: for HTML-mixed PHP files (HTML with <?php ... ?> tags)
12
- const require = createRequire(import.meta.url);
13
- const PHPModule = require("tree-sitter-php");
14
- const PHP_ONLY = PHPModule.php_only;
15
- const PHP_MIXED = PHPModule.php;
16
- // Dart Analysis Server integration
17
- // eslint-disable-next-line import/order
18
- import { analyzeDartSource } from "./dart/analyze.js";
19
- const SUPPORTED_LANGUAGES = new Set(["TypeScript", "Swift", "PHP", "Java", "Dart"]);
20
- function sanitizeSignature(source, node) {
21
- const start = node.getStart(source);
22
- const endCandidate = node.forEachChild((child) => {
23
- if (ts.isBlock(child) || ts.isModuleBlock(child)) {
24
- return child.getFullStart();
25
- }
26
- return undefined;
27
- });
28
- const end = typeof endCandidate === "number" ? endCandidate : node.getEnd();
29
- const snippet = source.text.slice(start, Math.min(end, start + 200));
30
- return snippet.split(/\r?\n/)[0]?.trim().replace(/\s+/g, " ") ?? "";
31
- }
32
- function getDocComment(node) {
33
- const tags = ts.getJSDocCommentsAndTags(node);
34
- const parts = [];
35
- for (const tag of tags) {
36
- if (ts.isJSDoc(tag)) {
37
- if (typeof tag.comment === "string") {
38
- parts.push(tag.comment);
39
- }
40
- else if (Array.isArray(tag.comment)) {
41
- parts.push(tag.comment.map((part) => (typeof part === "string" ? part : part.text)).join(""));
42
- }
43
- }
44
- }
45
- if (parts.length === 0) {
46
- return null;
47
- }
48
- return parts.join("\n").trim() || null;
49
- }
50
- function toLineNumber(source, position) {
51
- return source.getLineAndCharacterOfPosition(position).line + 1;
52
- }
53
1
  /**
54
- * Swiftのシグネチャをサニタイズ(本体を除外し、最初の200文字に制限)
55
- */
56
- function sanitizeSwiftSignature(node, content) {
57
- const nodeText = content.substring(node.startIndex, node.endIndex);
58
- // 関数本体({...})を除外
59
- const bodyIndex = nodeText.indexOf("{");
60
- const signatureText = bodyIndex >= 0 ? nodeText.substring(0, bodyIndex) : nodeText;
61
- // 最初の200文字に制限し、1行に圧縮
62
- const truncated = signatureText.substring(0, 200);
63
- return truncated.split(/\r?\n/)[0]?.trim().replace(/\s+/g, " ") ?? "";
64
- }
65
- /**
66
- * Swiftのドキュメントコメント(/// または /** */)を抽出
67
- */
68
- function getSwiftDocComment(node, content) {
69
- const parent = node.parent;
70
- if (!parent)
71
- return null;
72
- // 親ノードのすべての子から、このノードの直前にあるコメントを探す
73
- const precedingComments = [];
74
- const siblings = parent.children; // namedChildrenではなくchildrenを使用
75
- const nodeIndex = siblings.indexOf(node);
76
- // このノードより前の兄弟を逆順で調べる
77
- for (let i = nodeIndex - 1; i >= 0; i--) {
78
- const sibling = siblings[i];
79
- if (!sibling)
80
- continue;
81
- // commentまたはmultiline_commentをチェック
82
- if (sibling.type === "comment" || sibling.type === "multiline_comment") {
83
- const commentText = content.substring(sibling.startIndex, sibling.endIndex);
84
- // /// または /** */ 形式のドキュメントコメントのみ抽出
85
- if (commentText.startsWith("///") || commentText.startsWith("/**")) {
86
- const cleanedComment = commentText
87
- .replace(/^\/\/\/\s?/gm, "")
88
- .replace(/^\/\*\*\s?|\s?\*\/$/g, "")
89
- .replace(/^\s*\*\s?/gm, "")
90
- .trim();
91
- precedingComments.unshift(cleanedComment);
92
- }
93
- }
94
- else if (sibling.type !== "{" &&
95
- sibling.type !== "}" &&
96
- !sibling.text.trim().match(/^\s*$/)) {
97
- // コメント以外の実質的なノードに到達したら終了
98
- break;
99
- }
100
- }
101
- if (precedingComments.length === 0) {
102
- return null;
103
- }
104
- return precedingComments.join("\n");
105
- }
106
- /**
107
- * tree-sitterの位置情報から行番号を取得(1-based)
108
- */
109
- function toSwiftLineNumber(position) {
110
- return position.row + 1;
111
- }
112
- /**
113
- * Detect whether PHP file is pure PHP or HTML-mixed.
114
- * Pure PHP: starts with <?php tag (possibly after whitespace, shebang, or BOM)
115
- * HTML-mixed: contains HTML content before <?php tag
2
+ * Code Intelligence API
116
3
  *
117
- * Handles:
118
- * - Case-insensitive PHP tags (<?php, <?PHP)
119
- * - Short tags (<?=)
120
- * - Shebangs (#!/usr/bin/env php)
121
- * - UTF-8 BOM (\uFEFF)
122
- */
123
- function detectPHPType(content) {
124
- // Regex to find the first PHP open tag, ignoring shebangs, whitespace, and BOM.
125
- // It checks for <?php (case-insensitive), <?=, or <? (short tag)
126
- const phpTagRegex = /^(?:\s*|#!\/.*?\n|\uFEFF)*<\?(?:php|=)?/i;
127
- const match = content.match(phpTagRegex);
128
- if (!match) {
129
- // No PHP tags found at the beginning - treat as HTML-mixed
130
- return "html-mixed";
131
- }
132
- // If the match occurs at or near the beginning (after only whitespace/shebang/BOM),
133
- // it's a pure PHP file
134
- return "pure";
135
- }
136
- /**
137
- * Sanitize PHP signature (exclude body, limit to first 200 characters)
138
- */
139
- function sanitizePHPSignature(node, content) {
140
- const nodeText = content.substring(node.startIndex, node.endIndex);
141
- // 関数本体({...})を除外
142
- const bodyIndex = nodeText.indexOf("{");
143
- const signatureText = bodyIndex >= 0 ? nodeText.substring(0, bodyIndex) : nodeText;
144
- // 最初の200文字に制限し、1行に圧縮
145
- const truncated = signatureText.substring(0, 200);
146
- return truncated.split(/\r?\n/)[0]?.trim().replace(/\s+/g, " ") ?? "";
147
- }
148
- /**
149
- * PHPDocコメント(/** */)を抽出
150
- */
151
- function getPHPDocComment(node, content) {
152
- const parent = node.parent;
153
- if (!parent)
154
- return null;
155
- // 親ノードのすべての子から、このノードの直前にあるコメントを探す
156
- const precedingComments = [];
157
- const siblings = parent.children;
158
- const nodeIndex = siblings.indexOf(node);
159
- // このノードより前の兄弟を逆順で調べる
160
- for (let i = nodeIndex - 1; i >= 0; i--) {
161
- const sibling = siblings[i];
162
- if (!sibling)
163
- continue;
164
- // commentをチェック
165
- if (sibling.type === "comment") {
166
- const commentText = content.substring(sibling.startIndex, sibling.endIndex);
167
- // /** */ 形式のPHPDocコメントのみ抽出
168
- if (commentText.startsWith("/**") && commentText.endsWith("*/")) {
169
- const cleanedComment = commentText
170
- .replace(/^\/\*\*\s?|\s?\*\/$/g, "")
171
- .replace(/^\s*\*\s?/gm, "")
172
- .trim();
173
- precedingComments.unshift(cleanedComment);
174
- }
175
- }
176
- else if (sibling.type !== "text" && !sibling.text.trim().match(/^\s*$/)) {
177
- // コメント以外の実質的なノードに到達したら終了
178
- break;
179
- }
180
- }
181
- if (precedingComments.length === 0) {
182
- return null;
183
- }
184
- return precedingComments.join("\n");
185
- }
186
- /**
187
- * tree-sitterの位置情報から行番号を取得(1-based)
188
- */
189
- function toPHPLineNumber(position) {
190
- return position.row + 1;
191
- }
192
- /**
193
- * PHPのシンボルレコードを作成
194
- */
195
- function createPHPSymbolRecords(tree, content) {
196
- const results = [];
197
- /**
198
- * 子ノードから名前を抽出
199
- */
200
- function extractName(node) {
201
- function findName(n) {
202
- if (n.type === "name") {
203
- return n;
204
- }
205
- for (const child of n.namedChildren) {
206
- const found = findName(child);
207
- if (found)
208
- return found;
209
- }
210
- return null;
211
- }
212
- const nameNode = findName(node);
213
- return nameNode ? content.substring(nameNode.startIndex, nameNode.endIndex) : null;
214
- }
215
- /**
216
- * ツリーを再帰的にトラバースしてシンボルを抽出
217
- */
218
- function visit(node) {
219
- // class_declaration: クラス宣言
220
- if (node.type === "class_declaration") {
221
- const name = extractName(node);
222
- if (name) {
223
- results.push({
224
- name,
225
- kind: "class",
226
- rangeStartLine: toPHPLineNumber(node.startPosition),
227
- rangeEndLine: toPHPLineNumber(node.endPosition),
228
- signature: sanitizePHPSignature(node, content),
229
- doc: getPHPDocComment(node, content),
230
- });
231
- }
232
- }
233
- // interface_declaration: インターフェース宣言
234
- else if (node.type === "interface_declaration") {
235
- const name = extractName(node);
236
- if (name) {
237
- results.push({
238
- name,
239
- kind: "interface",
240
- rangeStartLine: toPHPLineNumber(node.startPosition),
241
- rangeEndLine: toPHPLineNumber(node.endPosition),
242
- signature: sanitizePHPSignature(node, content),
243
- doc: getPHPDocComment(node, content),
244
- });
245
- }
246
- }
247
- // trait_declaration: トレイト宣言
248
- else if (node.type === "trait_declaration") {
249
- const name = extractName(node);
250
- if (name) {
251
- results.push({
252
- name,
253
- kind: "trait",
254
- rangeStartLine: toPHPLineNumber(node.startPosition),
255
- rangeEndLine: toPHPLineNumber(node.endPosition),
256
- signature: sanitizePHPSignature(node, content),
257
- doc: getPHPDocComment(node, content),
258
- });
259
- }
260
- }
261
- // function_definition: トップレベル関数またはメソッド
262
- else if (node.type === "function_definition" || node.type === "method_declaration") {
263
- const name = extractName(node);
264
- if (name) {
265
- // 親がclass_declaration/trait_declaration/interface_declarationの子孫の場合はmethod、それ以外はfunction
266
- let isMethod = false;
267
- let parentNode = node.parent;
268
- while (parentNode) {
269
- if (parentNode.type === "class_declaration" ||
270
- parentNode.type === "trait_declaration" ||
271
- parentNode.type === "interface_declaration") {
272
- isMethod = true;
273
- break;
274
- }
275
- parentNode = parentNode.parent;
276
- }
277
- const kind = isMethod ? "method" : "function";
278
- results.push({
279
- name,
280
- kind,
281
- rangeStartLine: toPHPLineNumber(node.startPosition),
282
- rangeEndLine: toPHPLineNumber(node.endPosition),
283
- signature: sanitizePHPSignature(node, content),
284
- doc: getPHPDocComment(node, content),
285
- });
286
- }
287
- }
288
- // property_declaration: プロパティ
289
- else if (node.type === "property_declaration") {
290
- // property_element から変数名を抽出
291
- const propertyElement = node.namedChildren.find((child) => child.type === "property_element");
292
- if (propertyElement) {
293
- const variableNameNode = propertyElement.namedChildren.find((child) => child.type === "variable_name");
294
- if (variableNameNode) {
295
- const name = content
296
- .substring(variableNameNode.startIndex, variableNameNode.endIndex)
297
- .replace(/^\$/, ""); // $を除去
298
- results.push({
299
- name,
300
- kind: "property",
301
- rangeStartLine: toPHPLineNumber(node.startPosition),
302
- rangeEndLine: toPHPLineNumber(node.endPosition),
303
- signature: sanitizePHPSignature(node, content),
304
- doc: getPHPDocComment(node, content),
305
- });
306
- }
307
- }
308
- }
309
- // const_declaration: 定数(クラス定数)
310
- else if (node.type === "const_declaration") {
311
- const constElement = node.namedChildren.find((child) => child.type === "const_element");
312
- if (constElement) {
313
- const name = extractName(constElement);
314
- if (name) {
315
- results.push({
316
- name,
317
- kind: "constant",
318
- rangeStartLine: toPHPLineNumber(node.startPosition),
319
- rangeEndLine: toPHPLineNumber(node.endPosition),
320
- signature: sanitizePHPSignature(node, content),
321
- doc: getPHPDocComment(node, content),
322
- });
323
- }
324
- }
325
- }
326
- // namespace_definition: 名前空間
327
- else if (node.type === "namespace_definition") {
328
- const namespaceName = node.namedChildren.find((child) => child.type === "namespace_name");
329
- if (namespaceName) {
330
- const name = content.substring(namespaceName.startIndex, namespaceName.endIndex);
331
- results.push({
332
- name,
333
- kind: "namespace",
334
- rangeStartLine: toPHPLineNumber(node.startPosition),
335
- rangeEndLine: toPHPLineNumber(node.endPosition),
336
- signature: sanitizePHPSignature(node, content),
337
- doc: getPHPDocComment(node, content),
338
- });
339
- }
340
- }
341
- // 子ノードを再帰的に訪問
342
- for (const child of node.namedChildren) {
343
- visit(child);
344
- }
345
- }
346
- visit(tree.rootNode);
347
- return results
348
- .sort((a, b) => a.rangeStartLine - b.rangeStartLine)
349
- .map((item, index) => ({ symbolId: index + 1, ...item }));
350
- }
351
- /**
352
- * PHPのuse文を解析して依存関係を収集
353
- */
354
- function collectPHPDependencies(sourcePath, tree, content, _fileSet) {
355
- const dependencies = new Map();
356
- const record = (kind, dst) => {
357
- const key = `${kind}:${dst}`;
358
- if (!dependencies.has(key)) {
359
- dependencies.set(key, { dstKind: kind, dst, rel: "import" });
360
- }
361
- };
362
- function visit(node) {
363
- // namespace_use_declaration: use文
364
- if (node.type === "namespace_use_declaration") {
365
- // namespace_use_clauseを探す(通常のuse文)
366
- const useClauses = node.namedChildren.filter((child) => child.type === "namespace_use_clause");
367
- for (const useClause of useClauses) {
368
- // qualified_nameまたはnameを抽出
369
- const qualifiedName = useClause.namedChildren.find((child) => child.type === "qualified_name" || child.type === "name");
370
- if (qualifiedName) {
371
- const importName = content.substring(qualifiedName.startIndex, qualifiedName.endIndex);
372
- // Treat all namespaced imports as packages until composer.json parsing is implemented.
373
- // PSR-4 maps namespace prefixes to base directories (e.g., App\ -> src/),
374
- // not relative paths from the current file's directory.
375
- // TODO: Implement proper PSR-4 resolution by parsing composer.json
376
- record("package", importName);
377
- }
378
- }
379
- // namespace_use_groupを探す(グループ化されたuse文: use Foo\{Bar, Baz};)
380
- const useGroups = node.namedChildren.filter((child) => child.type === "namespace_use_group");
381
- if (useGroups.length > 0) {
382
- // プレフィックスを取得(例: App\Services)
383
- const prefixNode = node.namedChildren.find((child) => child.type === "namespace_name");
384
- const prefix = prefixNode
385
- ? content.substring(prefixNode.startIndex, prefixNode.endIndex)
386
- : "";
387
- for (const useGroup of useGroups) {
388
- // namespace_use_group_clauseを処理
389
- const groupClauses = useGroup.namedChildren.filter((child) => child.type === "namespace_use_group_clause");
390
- for (const groupClause of groupClauses) {
391
- const namespaceName = groupClause.namedChildren.find((child) => child.type === "namespace_name" || child.type === "name");
392
- if (namespaceName) {
393
- const suffix = content.substring(namespaceName.startIndex, namespaceName.endIndex);
394
- const fullName = prefix ? `${prefix}\\${suffix}` : suffix;
395
- // Treat all namespaced imports as packages (same reasoning as above)
396
- record("package", fullName);
397
- }
398
- }
399
- }
400
- }
401
- }
402
- // 子ノードを再帰的に訪問
403
- for (const child of node.namedChildren) {
404
- visit(child);
405
- }
406
- }
407
- visit(tree.rootNode);
408
- return Array.from(dependencies.values());
409
- }
410
- /**
411
- * Javaのシグネチャをサニタイズ(本体を除外し、最初の200文字に制限)
412
- */
413
- function sanitizeJavaSignature(node, content) {
414
- const nodeText = content.substring(node.startIndex, node.endIndex);
415
- // メソッド本体({...})を除外
416
- const bodyIndex = nodeText.indexOf("{");
417
- const signatureText = bodyIndex >= 0 ? nodeText.substring(0, bodyIndex) : nodeText;
418
- // 改行を空白に置き換えて1行に圧縮してから200文字に制限
419
- // (先に改行処理することで、複数行シグネチャの情報が失われるのを防ぐ)
420
- const normalized = signatureText.replace(/\s+/g, " ").trim();
421
- return normalized.substring(0, 200);
422
- }
423
- /**
424
- * Javadocコメント(/** */)を抽出
425
- */
426
- function getJavaDocComment(node, content) {
427
- const parent = node.parent;
428
- if (!parent)
429
- return null;
430
- // 親ノードのすべての子から、このノードの直前にあるコメントを探す
431
- const precedingComments = [];
432
- const siblings = parent.children;
433
- const nodeIndex = siblings.indexOf(node);
434
- // このノードより前の兄弟を逆順で調べる
435
- for (let i = nodeIndex - 1; i >= 0; i--) {
436
- const sibling = siblings[i];
437
- if (!sibling)
438
- continue;
439
- // block_commentをチェック
440
- if (sibling.type === "block_comment") {
441
- const commentText = content.substring(sibling.startIndex, sibling.endIndex);
442
- // /** */ 形式のJavadocコメントのみ抽出
443
- if (commentText.startsWith("/**") && commentText.endsWith("*/")) {
444
- const cleanedComment = commentText
445
- .replace(/^\/\*\*\s?|\s?\*\/$/g, "")
446
- .replace(/^\s*\*\s?/gm, "")
447
- .trim();
448
- precedingComments.unshift(cleanedComment);
449
- }
450
- }
451
- else if (!sibling.text.trim().match(/^\s*$/)) {
452
- // コメント以外の実質的なノードに到達したら終了
453
- break;
454
- }
455
- }
456
- if (precedingComments.length === 0) {
457
- return null;
458
- }
459
- return precedingComments.join("\n");
460
- }
461
- /**
462
- * tree-sitterの位置情報から行番号を取得(1-based)
463
- */
464
- function toJavaLineNumber(position) {
465
- return position.row + 1;
466
- }
467
- /**
468
- * Javaのシンボルレコードを作成
469
- */
470
- function createJavaSymbolRecords(tree, content) {
471
- const results = [];
472
- /**
473
- * 子ノードから識別子名を抽出
474
- */
475
- function extractName(node) {
476
- const identifierNode = node.namedChildren.find((child) => child.type === "identifier");
477
- return identifierNode
478
- ? content.substring(identifierNode.startIndex, identifierNode.endIndex)
479
- : null;
480
- }
481
- /**
482
- * ツリーを再帰的にトラバースしてシンボルを抽出
483
- */
484
- function visit(node) {
485
- // class_declaration: クラス宣言
486
- if (node.type === "class_declaration") {
487
- const name = extractName(node);
488
- if (name) {
489
- results.push({
490
- name,
491
- kind: "class",
492
- rangeStartLine: toJavaLineNumber(node.startPosition),
493
- rangeEndLine: toJavaLineNumber(node.endPosition),
494
- signature: sanitizeJavaSignature(node, content),
495
- doc: getJavaDocComment(node, content),
496
- });
497
- }
498
- }
499
- // interface_declaration: インターフェース宣言
500
- if (node.type === "interface_declaration") {
501
- const name = extractName(node);
502
- if (name) {
503
- results.push({
504
- name,
505
- kind: "interface",
506
- rangeStartLine: toJavaLineNumber(node.startPosition),
507
- rangeEndLine: toJavaLineNumber(node.endPosition),
508
- signature: sanitizeJavaSignature(node, content),
509
- doc: getJavaDocComment(node, content),
510
- });
511
- }
512
- }
513
- // enum_declaration: 列挙型宣言
514
- if (node.type === "enum_declaration") {
515
- const name = extractName(node);
516
- if (name) {
517
- results.push({
518
- name,
519
- kind: "enum",
520
- rangeStartLine: toJavaLineNumber(node.startPosition),
521
- rangeEndLine: toJavaLineNumber(node.endPosition),
522
- signature: sanitizeJavaSignature(node, content),
523
- doc: getJavaDocComment(node, content),
524
- });
525
- }
526
- }
527
- // annotation_type_declaration: アノテーション型宣言
528
- if (node.type === "annotation_type_declaration") {
529
- const name = extractName(node);
530
- if (name) {
531
- results.push({
532
- name,
533
- kind: "annotation",
534
- rangeStartLine: toJavaLineNumber(node.startPosition),
535
- rangeEndLine: toJavaLineNumber(node.endPosition),
536
- signature: sanitizeJavaSignature(node, content),
537
- doc: getJavaDocComment(node, content),
538
- });
539
- }
540
- }
541
- // field_declaration: フィールド宣言
542
- if (node.type === "field_declaration") {
543
- // variable_declaratorから変数名を抽出
544
- const declarators = node.namedChildren.filter((child) => child.type === "variable_declarator");
545
- for (const declarator of declarators) {
546
- const name = extractName(declarator);
547
- if (name) {
548
- results.push({
549
- name,
550
- kind: "field",
551
- rangeStartLine: toJavaLineNumber(node.startPosition),
552
- rangeEndLine: toJavaLineNumber(node.endPosition),
553
- signature: sanitizeJavaSignature(node, content),
554
- doc: getJavaDocComment(node, content),
555
- });
556
- }
557
- }
558
- }
559
- // constructor_declaration: コンストラクタ宣言
560
- if (node.type === "constructor_declaration") {
561
- const name = extractName(node);
562
- if (name) {
563
- results.push({
564
- name,
565
- kind: "constructor",
566
- rangeStartLine: toJavaLineNumber(node.startPosition),
567
- rangeEndLine: toJavaLineNumber(node.endPosition),
568
- signature: sanitizeJavaSignature(node, content),
569
- doc: getJavaDocComment(node, content),
570
- });
571
- }
572
- }
573
- // method_declaration: メソッド宣言
574
- if (node.type === "method_declaration") {
575
- const name = extractName(node);
576
- if (name) {
577
- results.push({
578
- name,
579
- kind: "method",
580
- rangeStartLine: toJavaLineNumber(node.startPosition),
581
- rangeEndLine: toJavaLineNumber(node.endPosition),
582
- signature: sanitizeJavaSignature(node, content),
583
- doc: getJavaDocComment(node, content),
584
- });
585
- }
586
- }
587
- // annotation_type_element_declaration: アノテーション内のメソッド宣言
588
- if (node.type === "annotation_type_element_declaration") {
589
- const name = extractName(node);
590
- if (name) {
591
- results.push({
592
- name,
593
- kind: "method",
594
- rangeStartLine: toJavaLineNumber(node.startPosition),
595
- rangeEndLine: toJavaLineNumber(node.endPosition),
596
- signature: sanitizeJavaSignature(node, content),
597
- doc: getJavaDocComment(node, content),
598
- });
599
- }
600
- }
601
- // 子ノードを再帰的に訪問
602
- for (const child of node.namedChildren) {
603
- visit(child);
604
- }
605
- }
606
- visit(tree.rootNode);
607
- // symbolIdを割り当て
608
- return results
609
- .sort((a, b) => a.rangeStartLine - b.rangeStartLine)
610
- .map((item, index) => ({ symbolId: index + 1, ...item }));
611
- }
612
- /**
613
- * Javaのimport文を解析して依存関係を収集
614
- */
615
- function collectJavaDependencies(_sourcePath, tree, content, fileSet) {
616
- const dependencies = new Map();
617
- const record = (kind, dst) => {
618
- const key = `${kind}:${dst}`;
619
- if (!dependencies.has(key)) {
620
- dependencies.set(key, { dstKind: kind, dst, rel: "import" });
621
- }
622
- };
623
- function visit(node) {
624
- // import_declaration: import文
625
- if (node.type === "import_declaration") {
626
- // scoped_identifier または identifier を探す
627
- const identifierNode = node.namedChildren.find((child) => child.type === "scoped_identifier" || child.type === "identifier");
628
- if (identifierNode) {
629
- let importName = content.substring(identifierNode.startIndex, identifierNode.endIndex);
630
- // ワイルドカードインポートのチェック (import java.util.*)
631
- // asteriskノードを探す
632
- const hasAsterisk = node.namedChildren.some((child) => child.type === "asterisk");
633
- if (hasAsterisk) {
634
- importName += ".*";
635
- }
636
- // ローカルファイル判定: com.example.MyClass -> com/example/MyClass.java
637
- // ワイルドカードの場合はパッケージとして扱う
638
- let kind = "package";
639
- if (!hasAsterisk) {
640
- const filePath = importName.replace(/\./g, "/") + ".java";
641
- // Maven/Gradle標準構造のプレフィックスを除去して完全一致でチェック
642
- // これにより、同名ファイルが異なるパッケージに存在する場合も正確に解決できる
643
- const matchingFile = Array.from(fileSet).find((f) => {
644
- // 標準的なソースディレクトリプレフィックスを除去
645
- const normalizedPath = f
646
- .replace(/^src\/main\/java\//, "")
647
- .replace(/^src\/test\/java\//, "")
648
- .replace(/^src\//, "");
649
- return normalizedPath === filePath;
650
- });
651
- kind = matchingFile ? "path" : "package";
652
- }
653
- record(kind, importName);
654
- }
655
- }
656
- // 子ノードを再帰的に訪問
657
- for (const child of node.namedChildren) {
658
- visit(child);
659
- }
660
- }
661
- visit(tree.rootNode);
662
- return Array.from(dependencies.values());
663
- }
664
- function createSymbolRecords(source) {
665
- const results = [];
666
- const visit = (node) => {
667
- if (ts.isFunctionDeclaration(node) && node.name) {
668
- results.push({
669
- name: node.name.getText(source),
670
- kind: "function",
671
- rangeStartLine: toLineNumber(source, node.getStart(source)),
672
- rangeEndLine: toLineNumber(source, node.getEnd()),
673
- signature: sanitizeSignature(source, node),
674
- doc: getDocComment(node),
675
- });
676
- }
677
- else if (ts.isClassDeclaration(node) && node.name) {
678
- results.push({
679
- name: node.name.getText(source),
680
- kind: "class",
681
- rangeStartLine: toLineNumber(source, node.getStart(source)),
682
- rangeEndLine: toLineNumber(source, node.getEnd()),
683
- signature: sanitizeSignature(source, node),
684
- doc: getDocComment(node),
685
- });
686
- }
687
- else if (ts.isInterfaceDeclaration(node) && node.name) {
688
- results.push({
689
- name: node.name.getText(source),
690
- kind: "interface",
691
- rangeStartLine: toLineNumber(source, node.getStart(source)),
692
- rangeEndLine: toLineNumber(source, node.getEnd()),
693
- signature: sanitizeSignature(source, node),
694
- doc: getDocComment(node),
695
- });
696
- }
697
- else if (ts.isEnumDeclaration(node) && node.name) {
698
- results.push({
699
- name: node.name.getText(source),
700
- kind: "enum",
701
- rangeStartLine: toLineNumber(source, node.getStart(source)),
702
- rangeEndLine: toLineNumber(source, node.getEnd()),
703
- signature: sanitizeSignature(source, node),
704
- doc: getDocComment(node),
705
- });
706
- }
707
- else if (ts.isMethodDeclaration(node) && node.name) {
708
- const name = node.name.getText(source);
709
- results.push({
710
- name,
711
- kind: "method",
712
- rangeStartLine: toLineNumber(source, node.getStart(source)),
713
- rangeEndLine: toLineNumber(source, node.getEnd()),
714
- signature: sanitizeSignature(source, node),
715
- doc: getDocComment(node),
716
- });
717
- }
718
- ts.forEachChild(node, visit);
719
- };
720
- ts.forEachChild(source, visit);
721
- return results
722
- .sort((a, b) => a.rangeStartLine - b.rangeStartLine)
723
- .map((item, index) => ({ symbolId: index + 1, ...item }));
724
- }
725
- /**
726
- * Swiftのシンボルレコードを作成
727
- */
728
- function createSwiftSymbolRecords(tree, content) {
729
- const results = [];
730
- /**
731
- * 子ノードから識別子名を抽出(再帰的に探索)
732
- */
733
- function extractName(node) {
734
- function findIdentifier(n) {
735
- if (n.type === "type_identifier" || n.type === "simple_identifier") {
736
- return n;
737
- }
738
- for (const child of n.namedChildren) {
739
- const found = findIdentifier(child);
740
- if (found)
741
- return found;
742
- }
743
- return null;
744
- }
745
- const identifierNode = findIdentifier(node);
746
- return identifierNode
747
- ? content.substring(identifierNode.startIndex, identifierNode.endIndex)
748
- : null;
749
- }
750
- /**
751
- * class_declarationのキーワードから種類を判定
752
- */
753
- function getClassDeclKind(node) {
754
- for (const child of node.children) {
755
- if (child.type === "class")
756
- return "class";
757
- if (child.type === "struct")
758
- return "struct";
759
- if (child.type === "enum")
760
- return "enum";
761
- if (child.type === "extension")
762
- return "extension";
763
- }
764
- return null;
765
- }
766
- /**
767
- * ツリーを再帰的にトラバースしてシンボルを抽出
768
- */
769
- function visit(node) {
770
- // class_declaration: class/struct/enum/extension
771
- if (node.type === "class_declaration") {
772
- const kind = getClassDeclKind(node);
773
- const name = extractName(node);
774
- if (kind && name) {
775
- results.push({
776
- name,
777
- kind,
778
- rangeStartLine: toSwiftLineNumber(node.startPosition),
779
- rangeEndLine: toSwiftLineNumber(node.endPosition),
780
- signature: sanitizeSwiftSignature(node, content),
781
- doc: getSwiftDocComment(node, content),
782
- });
783
- }
784
- }
785
- // protocol_declaration
786
- else if (node.type === "protocol_declaration") {
787
- const name = extractName(node);
788
- if (name) {
789
- results.push({
790
- name,
791
- kind: "protocol",
792
- rangeStartLine: toSwiftLineNumber(node.startPosition),
793
- rangeEndLine: toSwiftLineNumber(node.endPosition),
794
- signature: sanitizeSwiftSignature(node, content),
795
- doc: getSwiftDocComment(node, content),
796
- });
797
- }
798
- }
799
- // function_declaration: トップレベル関数またはメソッド
800
- else if (node.type === "function_declaration") {
801
- const name = extractName(node);
802
- if (name) {
803
- // 親がclass_bodyの場合はmethod、それ以外はfunction
804
- const kind = node.parent?.type === "class_body" ? "method" : "function";
805
- results.push({
806
- name,
807
- kind,
808
- rangeStartLine: toSwiftLineNumber(node.startPosition),
809
- rangeEndLine: toSwiftLineNumber(node.endPosition),
810
- signature: sanitizeSwiftSignature(node, content),
811
- doc: getSwiftDocComment(node, content),
812
- });
813
- }
814
- }
815
- // init_declaration: イニシャライザ
816
- else if (node.type === "init_declaration") {
817
- results.push({
818
- name: "init",
819
- kind: "initializer",
820
- rangeStartLine: toSwiftLineNumber(node.startPosition),
821
- rangeEndLine: toSwiftLineNumber(node.endPosition),
822
- signature: sanitizeSwiftSignature(node, content),
823
- doc: getSwiftDocComment(node, content),
824
- });
825
- }
826
- // property_declaration: プロパティ(クラス/構造体/プロトコル/エクステンション内のみ)
827
- else if (node.type === "property_declaration") {
828
- // 親がclass_body、struct_body、protocol_body、enum_class_body、extension_bodyの場合のみプロパティとして扱う
829
- const isClassMember = node.parent?.type === "class_body" ||
830
- node.parent?.type === "struct_body" ||
831
- node.parent?.type === "protocol_body" ||
832
- node.parent?.type === "enum_class_body" ||
833
- node.parent?.type === "extension_body";
834
- if (isClassMember) {
835
- const name = extractName(node);
836
- if (name) {
837
- results.push({
838
- name,
839
- kind: "property",
840
- rangeStartLine: toSwiftLineNumber(node.startPosition),
841
- rangeEndLine: toSwiftLineNumber(node.endPosition),
842
- signature: sanitizeSwiftSignature(node, content),
843
- doc: getSwiftDocComment(node, content),
844
- });
845
- }
846
- }
847
- }
848
- // protocol_function_declaration: プロトコル内のメソッド宣言
849
- else if (node.type === "protocol_function_declaration") {
850
- const name = extractName(node);
851
- if (name) {
852
- results.push({
853
- name,
854
- kind: "protocol_method",
855
- rangeStartLine: toSwiftLineNumber(node.startPosition),
856
- rangeEndLine: toSwiftLineNumber(node.endPosition),
857
- signature: sanitizeSwiftSignature(node, content),
858
- doc: getSwiftDocComment(node, content),
859
- });
860
- }
861
- }
862
- // 子ノードを再帰的に訪問
863
- for (const child of node.namedChildren) {
864
- visit(child);
865
- }
866
- }
867
- visit(tree.rootNode);
868
- return results
869
- .sort((a, b) => a.rangeStartLine - b.rangeStartLine)
870
- .map((item, index) => ({ symbolId: index + 1, ...item }));
871
- }
872
- function normalizePathSpecifier(sourcePath, specifier, fileSet) {
873
- if (specifier.startsWith(".") || specifier.startsWith("/")) {
874
- const baseDir = path.posix.dirname(sourcePath);
875
- // Strip .js extension if present (ESM imports use .js but source files are .ts)
876
- const pathWithoutJs = specifier.endsWith(".js") ? specifier.slice(0, -3) : specifier;
877
- const joined = path.posix.normalize(path.posix.join(baseDir, pathWithoutJs));
878
- const candidates = [
879
- joined,
880
- `${joined}.ts`,
881
- `${joined}.tsx`,
882
- `${joined}.js`,
883
- `${joined}.jsx`,
884
- `${joined}/index.ts`,
885
- `${joined}/index.tsx`,
886
- ];
887
- for (const candidate of candidates) {
888
- if (fileSet.has(candidate)) {
889
- return { kind: "path", target: candidate };
890
- }
891
- }
892
- return null;
893
- }
894
- return { kind: "package", target: specifier };
895
- }
896
- function collectDependencies(sourcePath, source, fileSet) {
897
- const dependencies = new Map();
898
- const record = (kind, dst) => {
899
- const key = `${kind}:${dst}`;
900
- if (!dependencies.has(key)) {
901
- dependencies.set(key, { dstKind: kind, dst, rel: "import" });
902
- }
903
- };
904
- const visit = (node) => {
905
- if (ts.isImportDeclaration(node) &&
906
- node.moduleSpecifier &&
907
- ts.isStringLiteral(node.moduleSpecifier)) {
908
- const target = normalizePathSpecifier(sourcePath, node.moduleSpecifier.text, fileSet);
909
- if (target) {
910
- record(target.kind, target.target);
911
- }
912
- }
913
- else if (ts.isExportDeclaration(node) &&
914
- node.moduleSpecifier &&
915
- ts.isStringLiteral(node.moduleSpecifier)) {
916
- const target = normalizePathSpecifier(sourcePath, node.moduleSpecifier.text, fileSet);
917
- if (target) {
918
- record(target.kind, target.target);
919
- }
920
- }
921
- else if (ts.isCallExpression(node)) {
922
- const firstArg = node.arguments[0];
923
- if (node.expression.getText(source) === "require" &&
924
- node.arguments.length === 1 &&
925
- firstArg &&
926
- ts.isStringLiteral(firstArg)) {
927
- const target = normalizePathSpecifier(sourcePath, firstArg.text, fileSet);
928
- if (target) {
929
- record(target.kind, target.target);
930
- }
931
- }
932
- }
933
- ts.forEachChild(node, visit);
934
- };
935
- ts.forEachChild(source, visit);
936
- return Array.from(dependencies.values());
937
- }
938
- /**
939
- * Swiftのimport文を解析して依存関係を収集
4
+ * 言語アナライザーシステムの公開APIを提供するファサード。
5
+ * 実際の解析処理は codeintel/ モジュールの LanguageRegistry に委譲。
6
+ *
7
+ * 使用例:
8
+ * ```typescript
9
+ * import { analyzeSource } from './codeintel.js';
10
+ *
11
+ * const result = await analyzeSource('src/index.ts', 'TypeScript', content, fileSet);
12
+ * ```
13
+ */
14
+ export { buildFallbackSnippet } from "./codeintel/utils.js";
15
+ // Registry と Analyzer をインポート
16
+ import { createDartAnalyzer } from "./codeintel/dart/index.js";
17
+ import { createJavaAnalyzer } from "./codeintel/java/index.js";
18
+ import { createPHPAnalyzer } from "./codeintel/php/index.js";
19
+ import { LanguageRegistry } from "./codeintel/registry.js";
20
+ import { createRustAnalyzer } from "./codeintel/rust/index.js";
21
+ import { createSwiftAnalyzer } from "./codeintel/swift/index.js";
22
+ import { createTypeScriptAnalyzer } from "./codeintel/typescript/index.js";
23
+ // シングルトンレジストリを初期化
24
+ const registry = LanguageRegistry.getInstance();
25
+ // 全言語アナライザーを登録
26
+ registry.register(createTypeScriptAnalyzer());
27
+ registry.register(createSwiftAnalyzer());
28
+ registry.register(createPHPAnalyzer());
29
+ registry.register(createJavaAnalyzer());
30
+ registry.register(createDartAnalyzer());
31
+ registry.register(createRustAnalyzer());
32
+ /**
33
+ * ソースコードを解析してシンボル、スニペット、依存関係を抽出
34
+ *
35
+ * @param pathInRepo - リポジトリ内のファイルパス
36
+ * @param lang - 言語名 (TypeScript, Swift, PHP, Java, Dart など)
37
+ * @param content - ファイルコンテンツ
38
+ * @param fileSet - リポジトリ内の全ファイルパスセット (依存関係解決用)
39
+ * @param workspaceRoot - ワークスペースルート (Dart 解析で必須)
40
+ * @returns シンボル、スニペット、依存関係を含む解析結果
940
41
  */
941
- function collectSwiftDependencies(sourcePath, tree, content, fileSet) {
942
- const dependencies = new Map();
943
- const record = (kind, dst) => {
944
- const key = `${kind}:${dst}`;
945
- if (!dependencies.has(key)) {
946
- dependencies.set(key, { dstKind: kind, dst, rel: "import" });
947
- }
948
- };
949
- function visit(node) {
950
- if (node.type === "import_declaration") {
951
- // import_declarationの子からidentifierを抽出
952
- const identifier = node.namedChildren.find((child) => child.type === "identifier");
953
- if (identifier) {
954
- const moduleName = content.substring(identifier.startIndex, identifier.endIndex);
955
- // Swiftファイルへの相対パスかどうかをチェック
956
- // 通常はシステムモジュール(Foundation, UIKit等)が多いため"package"として扱う
957
- const baseDir = path.posix.dirname(sourcePath);
958
- const swiftPath = path.posix.normalize(path.posix.join(baseDir, `${moduleName}.swift`));
959
- if (fileSet.has(swiftPath)) {
960
- record("path", swiftPath);
961
- }
962
- else {
963
- record("package", moduleName);
964
- }
965
- }
966
- }
967
- // 子ノードを再帰的に訪問
968
- for (const child of node.namedChildren) {
969
- visit(child);
970
- }
971
- }
972
- visit(tree.rootNode);
973
- return Array.from(dependencies.values());
974
- }
975
42
  export async function analyzeSource(pathInRepo, lang, content, fileSet, workspaceRoot) {
976
43
  const normalizedLang = lang ?? "";
977
- if (!SUPPORTED_LANGUAGES.has(normalizedLang)) {
44
+ // サポート対象言語かチェック
45
+ if (!registry.isSupported(normalizedLang)) {
978
46
  return { symbols: [], snippets: [], dependencies: [] };
979
47
  }
980
- // Dart language: use Analysis Server
981
- if (normalizedLang === "Dart") {
982
- if (!workspaceRoot) {
983
- console.warn(`[analyzeSource] workspaceRoot required for Dart analysis, skipping ${pathInRepo}`);
984
- return { symbols: [], snippets: [], dependencies: [] };
985
- }
986
- return await analyzeDartSource(pathInRepo, content, workspaceRoot);
987
- }
988
- // PHP language: use tree-sitter
989
- if (normalizedLang === "PHP") {
990
- try {
991
- // Create new parser instance for each file (thread-safety for concurrent processing)
992
- const parser = new Parser();
993
- // tree-sitter-php provides two parsers:
994
- // - php_only: for pure PHP files (<?php at start)
995
- // - php: for HTML-mixed PHP files (HTML with <?php ... ?> tags)
996
- const phpType = detectPHPType(content);
997
- const language = phpType === "html-mixed" ? PHP_MIXED : PHP_ONLY;
998
- // Validate language object before setting it
999
- if (!language || typeof language !== "object") {
1000
- throw new Error(`Tree-sitter language for PHP type "${phpType}" is invalid or undefined`);
1001
- }
1002
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1003
- parser.setLanguage(language);
1004
- const tree = parser.parse(content);
1005
- const symbols = createPHPSymbolRecords(tree, content);
1006
- const snippets = symbols.map((symbol) => ({
1007
- startLine: symbol.rangeStartLine,
1008
- endLine: symbol.rangeEndLine,
1009
- symbolId: symbol.symbolId,
1010
- }));
1011
- const dependencies = collectPHPDependencies(pathInRepo, tree, content, fileSet);
1012
- return { symbols, snippets, dependencies };
1013
- }
1014
- catch (error) {
1015
- // パース失敗時は空の結果を返して他のファイルの処理を継続
1016
- console.error(`Failed to parse PHP file ${pathInRepo}:`, error);
1017
- return { symbols: [], snippets: [], dependencies: [] };
1018
- }
1019
- }
1020
- // Java言語の場合、tree-sitterを使用
1021
- if (normalizedLang === "Java") {
1022
- try {
1023
- // 各ファイルごとに新しいパーサーインスタンスを作成(並行処理の安全性のため)
1024
- const parser = new Parser();
1025
- // Validate language object before setting it
1026
- if (!Java || typeof Java !== "object") {
1027
- throw new Error("Tree-sitter language for Java is invalid or undefined");
1028
- }
1029
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1030
- parser.setLanguage(Java);
1031
- const tree = parser.parse(content);
1032
- const symbols = createJavaSymbolRecords(tree, content);
1033
- const snippets = symbols.map((symbol) => ({
1034
- startLine: symbol.rangeStartLine,
1035
- endLine: symbol.rangeEndLine,
1036
- symbolId: symbol.symbolId,
1037
- }));
1038
- const dependencies = collectJavaDependencies(pathInRepo, tree, content, fileSet);
1039
- return { symbols, snippets, dependencies };
1040
- }
1041
- catch (error) {
1042
- // パース失敗時は空の結果を返して他のファイルの処理を継続
1043
- console.error(`Failed to parse Java file ${pathInRepo}:`, error);
1044
- return { symbols: [], snippets: [], dependencies: [] };
1045
- }
1046
- }
1047
- // Swift言語の場合、tree-sitterを使用
1048
- if (normalizedLang === "Swift") {
1049
- try {
1050
- // 各ファイルごとに新しいパーサーインスタンスを作成(並行処理の安全性のため)
1051
- const parser = new Parser();
1052
- // Validate language object before setting it
1053
- if (!Swift || typeof Swift !== "object") {
1054
- throw new Error("Tree-sitter language for Swift is invalid or undefined");
1055
- }
1056
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1057
- parser.setLanguage(Swift);
1058
- const tree = parser.parse(content);
1059
- const symbols = createSwiftSymbolRecords(tree, content);
1060
- const snippets = symbols.map((symbol) => ({
1061
- startLine: symbol.rangeStartLine,
1062
- endLine: symbol.rangeEndLine,
1063
- symbolId: symbol.symbolId,
1064
- }));
1065
- const dependencies = collectSwiftDependencies(pathInRepo, tree, content, fileSet);
1066
- return { symbols, snippets, dependencies };
1067
- }
1068
- catch (error) {
1069
- // パース失敗時は空の結果を返して他のファイルの処理を継続
1070
- console.error(`Failed to parse Swift file ${pathInRepo}:`, error);
1071
- return { symbols: [], snippets: [], dependencies: [] };
1072
- }
1073
- }
1074
- // TypeScript言語の場合、TypeScript Compiler APIを使用
1075
- const sourceFile = ts.createSourceFile(pathInRepo, content, ts.ScriptTarget.Latest, true);
1076
- const symbols = createSymbolRecords(sourceFile);
1077
- const snippets = symbols.map((symbol) => ({
1078
- startLine: symbol.rangeStartLine,
1079
- endLine: symbol.rangeEndLine,
1080
- symbolId: symbol.symbolId,
1081
- }));
1082
- const dependencies = collectDependencies(pathInRepo, sourceFile, fileSet);
1083
- return { symbols, snippets, dependencies };
48
+ // レジストリ経由で解析を実行
49
+ // exactOptionalPropertyTypes対応: workspaceRootはundefinedの場合は省略
50
+ return await registry.analyze(normalizedLang, {
51
+ pathInRepo,
52
+ content,
53
+ fileSet,
54
+ ...(workspaceRoot !== undefined && { workspaceRoot }),
55
+ });
1084
56
  }
1085
- export function buildFallbackSnippet(totalLines) {
1086
- return { startLine: 1, endLine: totalLines, symbolId: null };
57
+ /**
58
+ * 全言語アナライザーのリソースをクリーンアップ
59
+ *
60
+ * プロセス終了前に呼び出すことで、
61
+ * Dart Analysis Server などの外部プロセスを適切に終了
62
+ */
63
+ export async function cleanup() {
64
+ await registry.cleanup();
1087
65
  }
1088
66
  //# sourceMappingURL=codeintel.js.map