@wuyanbin/ai-code-review-cli-remote 1.0.36 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/context.d.ts +14 -3
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +47 -83
- package/dist/core/context.js.map +1 -1
- package/dist/core/diff.d.ts +1 -1
- package/dist/core/diff.js +3 -3
- package/dist/core/diff.js.map +1 -1
- package/dist/core/import-resolve.d.ts +62 -0
- package/dist/core/import-resolve.d.ts.map +1 -0
- package/dist/core/import-resolve.js +470 -0
- package/dist/core/import-resolve.js.map +1 -0
- package/dist/core/prompt.d.ts.map +1 -1
- package/dist/core/prompt.js +3 -1
- package/dist/core/prompt.js.map +1 -1
- package/dist/core/reviewer.d.ts.map +1 -1
- package/dist/core/reviewer.js +54 -3
- package/dist/core/reviewer.js.map +1 -1
- package/dist/core/swift-index.d.ts +45 -0
- package/dist/core/swift-index.d.ts.map +1 -0
- package/dist/core/swift-index.js +151 -0
- package/dist/core/swift-index.js.map +1 -0
- package/dist/core/symbol-context.d.ts +56 -0
- package/dist/core/symbol-context.d.ts.map +1 -0
- package/dist/core/symbol-context.js +511 -0
- package/dist/core/symbol-context.js.map +1 -0
- package/dist/core/symbols.d.ts +65 -0
- package/dist/core/symbols.d.ts.map +1 -0
- package/dist/core/symbols.js +1018 -0
- package/dist/core/symbols.js.map +1 -0
- package/dist/core/text-budget.d.ts +3 -0
- package/dist/core/text-budget.d.ts.map +1 -0
- package/dist/core/text-budget.js +14 -0
- package/dist/core/text-budget.js.map +1 -0
- package/dist/core/tree-sitter.d.ts +42 -0
- package/dist/core/tree-sitter.d.ts.map +1 -0
- package/dist/core/tree-sitter.js +160 -0
- package/dist/core/tree-sitter.js.map +1 -0
- package/dist/tree-sitter/tree-sitter-go.wasm +0 -0
- package/dist/tree-sitter/tree-sitter-java.wasm +0 -0
- package/dist/tree-sitter/tree-sitter-javascript.wasm +0 -0
- package/dist/tree-sitter/tree-sitter-kotlin.wasm +0 -0
- package/dist/tree-sitter/tree-sitter-swift.wasm +0 -0
- package/dist/tree-sitter/tree-sitter-tsx.wasm +0 -0
- package/dist/tree-sitter/tree-sitter-typescript.wasm +0 -0
- package/dist/tree-sitter/tree-sitter.js +4075 -0
- package/dist/tree-sitter/tree-sitter.wasm +0 -0
- package/package.json +5 -3
|
@@ -0,0 +1,1018 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_TYPE_LINES = void 0;
|
|
4
|
+
exports.classifyNode = classifyNode;
|
|
5
|
+
exports.enclosingDeclaration = enclosingDeclaration;
|
|
6
|
+
exports.goPackageNameCandidates = goPackageNameCandidates;
|
|
7
|
+
exports.collectImports = collectImports;
|
|
8
|
+
exports.packageName = packageName;
|
|
9
|
+
exports.collectReferences = collectReferences;
|
|
10
|
+
exports.findDeclaration = findDeclaration;
|
|
11
|
+
exports.renderRanges = renderRanges;
|
|
12
|
+
exports.findDeclarationByRegex = findDeclarationByRegex;
|
|
13
|
+
const tree_sitter_1 = require("./tree-sitter");
|
|
14
|
+
/** 类型 / 值 / 类成员清单的渲染行数上限 */
|
|
15
|
+
exports.MAX_TYPE_LINES = 40;
|
|
16
|
+
function field(node, name) {
|
|
17
|
+
try {
|
|
18
|
+
return node.childForFieldName(name);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function firstChild(node, ...types) {
|
|
25
|
+
return (0, tree_sitter_1.namedChildren)(node).find((c) => types.includes(c.type)) ?? null;
|
|
26
|
+
}
|
|
27
|
+
function hasAnonymousChild(node, type) {
|
|
28
|
+
return node.children.some((c) => c !== null && c.type === type);
|
|
29
|
+
}
|
|
30
|
+
function stripQuotes(text) {
|
|
31
|
+
return text.trim().replace(/^['"`]/, "").replace(/['"`]$/, "");
|
|
32
|
+
}
|
|
33
|
+
function samePosition(a, b) {
|
|
34
|
+
return (a.startPosition.row === b.startPosition.row &&
|
|
35
|
+
a.startPosition.column === b.startPosition.column &&
|
|
36
|
+
a.endPosition.row === b.endPosition.row &&
|
|
37
|
+
a.endPosition.column === b.endPosition.column);
|
|
38
|
+
}
|
|
39
|
+
function makeDeclaration(stmt, kind, name, body) {
|
|
40
|
+
const start = (0, tree_sitter_1.startLine)(stmt);
|
|
41
|
+
const header = body ? Math.max(start, (0, tree_sitter_1.startLine)(body)) : start;
|
|
42
|
+
return {
|
|
43
|
+
name,
|
|
44
|
+
kind,
|
|
45
|
+
startLine: start,
|
|
46
|
+
endLine: (0, tree_sitter_1.endLine)(stmt),
|
|
47
|
+
headerEndLine: Math.min(header, (0, tree_sitter_1.endLine)(stmt)),
|
|
48
|
+
node: stmt,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** `export function f` / `export const x`:把范围放大到 export 语句本身 */
|
|
52
|
+
function liftExport(stmt) {
|
|
53
|
+
return stmt.parent?.type === "export_statement" ? stmt.parent : stmt;
|
|
54
|
+
}
|
|
55
|
+
const TS_FUNCTION_STATEMENTS = new Set([
|
|
56
|
+
"function_declaration",
|
|
57
|
+
"generator_function_declaration",
|
|
58
|
+
"method_definition",
|
|
59
|
+
]);
|
|
60
|
+
const TS_FUNCTION_EXPRESSIONS = new Set([
|
|
61
|
+
"arrow_function",
|
|
62
|
+
"function_expression",
|
|
63
|
+
"generator_function",
|
|
64
|
+
"function",
|
|
65
|
+
]);
|
|
66
|
+
const TS_CLASS_LIKE = new Set(["class_declaration", "abstract_class_declaration"]);
|
|
67
|
+
/** 形状类声明:整体呈现(封顶 MAX_TYPE_LINES),不按成员拆分 */
|
|
68
|
+
const TS_TYPE_LIKE = new Set([
|
|
69
|
+
"interface_declaration",
|
|
70
|
+
"enum_declaration",
|
|
71
|
+
"internal_module",
|
|
72
|
+
"type_alias_declaration",
|
|
73
|
+
]);
|
|
74
|
+
const GO_FUNCTION = new Set(["function_declaration", "method_declaration"]);
|
|
75
|
+
const GO_VALUE = new Set(["const_declaration", "var_declaration"]);
|
|
76
|
+
const JAVA_FUNCTION = new Set([
|
|
77
|
+
"method_declaration",
|
|
78
|
+
"constructor_declaration",
|
|
79
|
+
]);
|
|
80
|
+
const JAVA_CLASS_LIKE = new Set(["class_declaration", "record_declaration"]);
|
|
81
|
+
const JAVA_TYPE_LIKE = new Set([
|
|
82
|
+
"interface_declaration",
|
|
83
|
+
"enum_declaration",
|
|
84
|
+
"annotation_type_declaration",
|
|
85
|
+
]);
|
|
86
|
+
/** 顶层按名字查找时,class 与 type 两类都算「类型声明」 */
|
|
87
|
+
const JAVA_TOP_LEVEL_TYPES = new Set([...JAVA_CLASS_LIKE, ...JAVA_TYPE_LIKE]);
|
|
88
|
+
/** Kotlin:class_declaration 同时覆盖 class / interface,靠匿名关键字与 class_modifier 区分形状类 */
|
|
89
|
+
const KOTLIN_FUNCTION = new Set([
|
|
90
|
+
"function_declaration",
|
|
91
|
+
"secondary_constructor",
|
|
92
|
+
"anonymous_initializer",
|
|
93
|
+
]);
|
|
94
|
+
const KOTLIN_OBJECT_LIKE = new Set(["object_declaration", "companion_object"]);
|
|
95
|
+
const KOTLIN_BODIES = ["class_body", "enum_class_body"];
|
|
96
|
+
/** Swift:class_declaration 同时覆盖 class / struct / enum / actor / extension */
|
|
97
|
+
const SWIFT_FUNCTION = new Set([
|
|
98
|
+
"function_declaration",
|
|
99
|
+
"init_declaration",
|
|
100
|
+
"deinit_declaration",
|
|
101
|
+
"subscript_declaration",
|
|
102
|
+
"protocol_function_declaration",
|
|
103
|
+
]);
|
|
104
|
+
const SWIFT_TYPE_LIKE = new Set(["protocol_declaration", "typealias_declaration"]);
|
|
105
|
+
const COMMENT_TYPES = new Set(["comment", "line_comment", "block_comment", "multiline_comment"]);
|
|
106
|
+
function anonymousChildren(node) {
|
|
107
|
+
return node.children
|
|
108
|
+
.filter((c) => c !== null && !c.isNamed)
|
|
109
|
+
.map((c) => c.type);
|
|
110
|
+
}
|
|
111
|
+
function isTopLevel(node) {
|
|
112
|
+
return node.parent?.type === "source_file";
|
|
113
|
+
}
|
|
114
|
+
function isCommentNode(node) {
|
|
115
|
+
return COMMENT_TYPES.has(node.type);
|
|
116
|
+
}
|
|
117
|
+
/** Kotlin 属性名:`val x` / `val (a, b)` / 扩展属性 `val String.x`(只取声明侧标识符) */
|
|
118
|
+
function kotlinPropertyNames(node) {
|
|
119
|
+
const names = [];
|
|
120
|
+
for (const child of (0, tree_sitter_1.namedChildren)(node)) {
|
|
121
|
+
if (child.type === "variable_declaration") {
|
|
122
|
+
const id = firstChild(child, "identifier");
|
|
123
|
+
if (id)
|
|
124
|
+
names.push(id.text);
|
|
125
|
+
}
|
|
126
|
+
else if (child.type === "multi_variable_declaration") {
|
|
127
|
+
for (const part of (0, tree_sitter_1.namedChildren)(child)) {
|
|
128
|
+
const id = part.type === "variable_declaration"
|
|
129
|
+
? firstChild(part, "identifier")
|
|
130
|
+
: part.type === "identifier"
|
|
131
|
+
? part
|
|
132
|
+
: null;
|
|
133
|
+
if (id)
|
|
134
|
+
names.push(id.text);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return names;
|
|
139
|
+
}
|
|
140
|
+
/** Swift 属性名:`name: pattern → bound_identifier`,元组模式可能有多个 */
|
|
141
|
+
function swiftPropertyNames(node) {
|
|
142
|
+
const names = [];
|
|
143
|
+
const visit = (n) => {
|
|
144
|
+
if (n.type === "simple_identifier") {
|
|
145
|
+
names.push(n.text);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
for (const c of (0, tree_sitter_1.namedChildren)(n))
|
|
149
|
+
visit(c);
|
|
150
|
+
};
|
|
151
|
+
for (const child of (0, tree_sitter_1.namedChildren)(node)) {
|
|
152
|
+
if (child.type === "pattern")
|
|
153
|
+
visit(child);
|
|
154
|
+
}
|
|
155
|
+
return names;
|
|
156
|
+
}
|
|
157
|
+
function isTsLike(language) {
|
|
158
|
+
return (language === "typescript" || language === "tsx" || language === "javascript");
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* 判断节点本身是否是一个声明;回调形式的函数表达式返回 null,让调用方继续向上找
|
|
162
|
+
* 真正的外层函数。
|
|
163
|
+
*/
|
|
164
|
+
function classifyNode(node, language) {
|
|
165
|
+
if (isTsLike(language)) {
|
|
166
|
+
if (TS_FUNCTION_STATEMENTS.has(node.type)) {
|
|
167
|
+
return makeDeclaration(liftExport(node), "function", field(node, "name")?.text ?? null, field(node, "body"));
|
|
168
|
+
}
|
|
169
|
+
if (TS_FUNCTION_EXPRESSIONS.has(node.type) || node.type === "class") {
|
|
170
|
+
const parent = node.parent;
|
|
171
|
+
if (!parent)
|
|
172
|
+
return null;
|
|
173
|
+
const kind = node.type === "class" ? "class" : "function";
|
|
174
|
+
const body = field(node, "body");
|
|
175
|
+
if (parent.type === "variable_declarator") {
|
|
176
|
+
const stmt = parent.parent ?? parent;
|
|
177
|
+
return makeDeclaration(liftExport(stmt), kind, field(parent, "name")?.text ?? null, body);
|
|
178
|
+
}
|
|
179
|
+
if (parent.type === "pair") {
|
|
180
|
+
return makeDeclaration(parent, kind, field(parent, "key")?.text ?? null, body);
|
|
181
|
+
}
|
|
182
|
+
if (parent.type === "assignment_expression") {
|
|
183
|
+
const stmt = parent.parent?.type === "expression_statement" ? parent.parent : parent;
|
|
184
|
+
return makeDeclaration(stmt, kind, field(parent, "left")?.text ?? null, body);
|
|
185
|
+
}
|
|
186
|
+
if (parent.type === "public_field_definition") {
|
|
187
|
+
return makeDeclaration(parent, kind, field(parent, "name")?.text ?? null, body);
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
if (TS_CLASS_LIKE.has(node.type)) {
|
|
192
|
+
return makeDeclaration(liftExport(node), "class", field(node, "name")?.text ?? null, field(node, "body"));
|
|
193
|
+
}
|
|
194
|
+
if (TS_TYPE_LIKE.has(node.type)) {
|
|
195
|
+
return makeDeclaration(liftExport(node), "type", field(node, "name")?.text ?? null, null);
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
if (language === "go") {
|
|
200
|
+
if (GO_FUNCTION.has(node.type)) {
|
|
201
|
+
return makeDeclaration(node, "function", field(node, "name")?.text ?? null, field(node, "body"));
|
|
202
|
+
}
|
|
203
|
+
if (node.type === "type_declaration") {
|
|
204
|
+
const spec = firstChild(node, "type_spec", "type_alias");
|
|
205
|
+
return makeDeclaration(node, "type", spec ? field(spec, "name")?.text ?? null : null, null);
|
|
206
|
+
}
|
|
207
|
+
if (GO_VALUE.has(node.type) && node.parent?.type === "source_file") {
|
|
208
|
+
const spec = firstChild(node, "const_spec", "var_spec");
|
|
209
|
+
return makeDeclaration(node, "value", spec ? field(spec, "name")?.text ?? null : null, null);
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
if (language === "java") {
|
|
214
|
+
if (JAVA_FUNCTION.has(node.type)) {
|
|
215
|
+
return makeDeclaration(node, "function", field(node, "name")?.text ?? null, field(node, "body"));
|
|
216
|
+
}
|
|
217
|
+
if (JAVA_CLASS_LIKE.has(node.type)) {
|
|
218
|
+
return makeDeclaration(node, "class", field(node, "name")?.text ?? null, field(node, "body"));
|
|
219
|
+
}
|
|
220
|
+
if (JAVA_TYPE_LIKE.has(node.type)) {
|
|
221
|
+
return makeDeclaration(node, "type", field(node, "name")?.text ?? null, null);
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
if (language === "kotlin") {
|
|
226
|
+
if (KOTLIN_FUNCTION.has(node.type)) {
|
|
227
|
+
const name = node.type === "secondary_constructor"
|
|
228
|
+
? "constructor"
|
|
229
|
+
: node.type === "anonymous_initializer"
|
|
230
|
+
? "init"
|
|
231
|
+
: field(node, "name")?.text ?? null;
|
|
232
|
+
return makeDeclaration(node, "function", name, firstChild(node, "function_body", "block"));
|
|
233
|
+
}
|
|
234
|
+
if (node.type === "class_declaration") {
|
|
235
|
+
const body = firstChild(node, ...KOTLIN_BODIES);
|
|
236
|
+
const name = field(node, "name")?.text ?? null;
|
|
237
|
+
// 无类体(data class / value class / 空类):顶层的整体呈现,嵌套的交给外层类
|
|
238
|
+
if (!body)
|
|
239
|
+
return isTopLevel(node) ? makeDeclaration(node, "type", name, null) : null;
|
|
240
|
+
const modifiers = firstChild(node, "modifiers");
|
|
241
|
+
const classModifiers = modifiers
|
|
242
|
+
? (0, tree_sitter_1.namedChildren)(modifiers).filter((m) => m.type === "class_modifier").map((m) => m.text)
|
|
243
|
+
: [];
|
|
244
|
+
const shapeLike = anonymousChildren(node).includes("interface") ||
|
|
245
|
+
body.type === "enum_class_body" ||
|
|
246
|
+
classModifiers.includes("enum") ||
|
|
247
|
+
classModifiers.includes("annotation");
|
|
248
|
+
return makeDeclaration(node, shapeLike ? "type" : "class", name, shapeLike ? null : body);
|
|
249
|
+
}
|
|
250
|
+
if (KOTLIN_OBJECT_LIKE.has(node.type)) {
|
|
251
|
+
const body = firstChild(node, "class_body");
|
|
252
|
+
const name = field(node, "name")?.text ?? (node.type === "companion_object" ? "Companion" : null);
|
|
253
|
+
if (!body)
|
|
254
|
+
return isTopLevel(node) ? makeDeclaration(node, "type", name, null) : null;
|
|
255
|
+
return makeDeclaration(node, "class", name, body);
|
|
256
|
+
}
|
|
257
|
+
if (node.type === "type_alias") {
|
|
258
|
+
const name = field(node, "type")?.text ?? firstChild(node, "identifier")?.text ?? null;
|
|
259
|
+
return makeDeclaration(node, "type", name, null);
|
|
260
|
+
}
|
|
261
|
+
if (node.type === "property_declaration") {
|
|
262
|
+
// 带 getter / setter 的属性像函数:签名 + 访问器体;普通成员属性交给外层类;顶层属性整体呈现
|
|
263
|
+
const accessor = firstChild(node, "getter", "setter");
|
|
264
|
+
const name = kotlinPropertyNames(node)[0] ?? null;
|
|
265
|
+
if (accessor)
|
|
266
|
+
return makeDeclaration(node, "function", name, accessor);
|
|
267
|
+
return isTopLevel(node) ? makeDeclaration(node, "value", name, null) : null;
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
if (language === "swift") {
|
|
272
|
+
if (SWIFT_FUNCTION.has(node.type)) {
|
|
273
|
+
const name = node.type === "init_declaration"
|
|
274
|
+
? "init"
|
|
275
|
+
: node.type === "deinit_declaration"
|
|
276
|
+
? "deinit"
|
|
277
|
+
: node.type === "subscript_declaration"
|
|
278
|
+
? "subscript"
|
|
279
|
+
: field(node, "name")?.text ?? null;
|
|
280
|
+
const body = field(node, "body") ?? firstChild(node, "function_body", "computed_property");
|
|
281
|
+
return makeDeclaration(node, "function", name, body);
|
|
282
|
+
}
|
|
283
|
+
if (node.type === "class_declaration") {
|
|
284
|
+
// extension 的 name 是 user_type,text 仍是类型名
|
|
285
|
+
const name = field(node, "name")?.text ?? null;
|
|
286
|
+
if (anonymousChildren(node).includes("enum"))
|
|
287
|
+
return makeDeclaration(node, "type", name, null);
|
|
288
|
+
return makeDeclaration(node, "class", name, field(node, "body"));
|
|
289
|
+
}
|
|
290
|
+
if (SWIFT_TYPE_LIKE.has(node.type)) {
|
|
291
|
+
return makeDeclaration(node, "type", field(node, "name")?.text ?? null, null);
|
|
292
|
+
}
|
|
293
|
+
if (node.type === "property_declaration") {
|
|
294
|
+
// 计算属性(SwiftUI 的 body)与 didSet 块像函数:签名 + 计算体;存储属性交给外层类型;顶层整体呈现
|
|
295
|
+
const computed = field(node, "computed_value") ?? firstChild(node, "computed_property", "willset_didset_block");
|
|
296
|
+
const name = swiftPropertyNames(node)[0] ?? null;
|
|
297
|
+
if (computed)
|
|
298
|
+
return makeDeclaration(node, "function", name, computed);
|
|
299
|
+
return isTopLevel(node) ? makeDeclaration(node, "value", name, null) : null;
|
|
300
|
+
}
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
/** 找包含指定行(1-based)的最内层声明;顶层语句、import 等返回 null */
|
|
306
|
+
function enclosingDeclaration(root, language, line, lineLength = 0) {
|
|
307
|
+
const row = line - 1;
|
|
308
|
+
if (row < 0 || row > root.endPosition.row)
|
|
309
|
+
return null;
|
|
310
|
+
let node = null;
|
|
311
|
+
try {
|
|
312
|
+
node =
|
|
313
|
+
root.descendantForPosition({ row, column: 0 }, { row, column: Math.max(0, lineLength) }) ?? root.descendantForPosition({ row, column: 0 });
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
for (let current = node; current; current = current.parent) {
|
|
319
|
+
const decl = classifyNode(current, language);
|
|
320
|
+
if (decl)
|
|
321
|
+
return decl;
|
|
322
|
+
}
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
/** Go 包名缺省取路径末段;`/v2` 版本段跳过;带连字符的目录再给两个常见变体 */
|
|
326
|
+
function goPackageNameCandidates(importPath) {
|
|
327
|
+
const segments = importPath.split("/").filter(Boolean);
|
|
328
|
+
let last = segments.pop() ?? importPath;
|
|
329
|
+
if (/^v\d+$/.test(last) && segments.length > 0)
|
|
330
|
+
last = segments.pop() ?? last;
|
|
331
|
+
const variants = new Set([
|
|
332
|
+
last,
|
|
333
|
+
last.replace(/^go-/, ""),
|
|
334
|
+
last.replace(/-go$/, ""),
|
|
335
|
+
last.replace(/-/g, ""),
|
|
336
|
+
last.replace(/[-.]/g, "_"),
|
|
337
|
+
]);
|
|
338
|
+
return [...variants].filter(Boolean);
|
|
339
|
+
}
|
|
340
|
+
function collectImports(root, language) {
|
|
341
|
+
const bindings = [];
|
|
342
|
+
if (isTsLike(language)) {
|
|
343
|
+
for (const stmt of (0, tree_sitter_1.namedChildren)(root)) {
|
|
344
|
+
if (stmt.type === "import_statement") {
|
|
345
|
+
const sourceNode = field(stmt, "source");
|
|
346
|
+
if (!sourceNode)
|
|
347
|
+
continue;
|
|
348
|
+
const source = stripQuotes(sourceNode.text);
|
|
349
|
+
const clause = firstChild(stmt, "import_clause");
|
|
350
|
+
if (!clause)
|
|
351
|
+
continue;
|
|
352
|
+
for (const part of (0, tree_sitter_1.namedChildren)(clause)) {
|
|
353
|
+
if (part.type === "identifier") {
|
|
354
|
+
bindings.push({ local: part.text, imported: "default", source, kind: "default" });
|
|
355
|
+
}
|
|
356
|
+
else if (part.type === "namespace_import") {
|
|
357
|
+
const id = firstChild(part, "identifier");
|
|
358
|
+
if (id)
|
|
359
|
+
bindings.push({ local: id.text, imported: null, source, kind: "namespace" });
|
|
360
|
+
}
|
|
361
|
+
else if (part.type === "named_imports") {
|
|
362
|
+
for (const spec of (0, tree_sitter_1.namedChildren)(part)) {
|
|
363
|
+
if (spec.type !== "import_specifier")
|
|
364
|
+
continue;
|
|
365
|
+
const name = field(spec, "name")?.text;
|
|
366
|
+
if (!name)
|
|
367
|
+
continue;
|
|
368
|
+
const alias = field(spec, "alias")?.text;
|
|
369
|
+
bindings.push({ local: alias ?? name, imported: name, source, kind: "named" });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (stmt.type === "lexical_declaration" || stmt.type === "variable_declaration") {
|
|
376
|
+
for (const declarator of (0, tree_sitter_1.namedChildren)(stmt)) {
|
|
377
|
+
if (declarator.type !== "variable_declarator")
|
|
378
|
+
continue;
|
|
379
|
+
const value = field(declarator, "value");
|
|
380
|
+
if (!value || value.type !== "call_expression")
|
|
381
|
+
continue;
|
|
382
|
+
if (field(value, "function")?.text !== "require")
|
|
383
|
+
continue;
|
|
384
|
+
const args = field(value, "arguments");
|
|
385
|
+
const str = args ? (0, tree_sitter_1.namedChildren)(args)[0] : null;
|
|
386
|
+
if (!str || str.type !== "string")
|
|
387
|
+
continue;
|
|
388
|
+
const source = stripQuotes(str.text);
|
|
389
|
+
const nameNode = field(declarator, "name");
|
|
390
|
+
if (!nameNode)
|
|
391
|
+
continue;
|
|
392
|
+
if (nameNode.type === "identifier") {
|
|
393
|
+
bindings.push({ local: nameNode.text, imported: null, source, kind: "namespace" });
|
|
394
|
+
}
|
|
395
|
+
else if (nameNode.type === "object_pattern") {
|
|
396
|
+
for (const prop of (0, tree_sitter_1.namedChildren)(nameNode)) {
|
|
397
|
+
if (prop.type === "shorthand_property_identifier_pattern") {
|
|
398
|
+
bindings.push({ local: prop.text, imported: prop.text, source, kind: "named" });
|
|
399
|
+
}
|
|
400
|
+
else if (prop.type === "pair_pattern") {
|
|
401
|
+
const key = field(prop, "key")?.text;
|
|
402
|
+
const local = field(prop, "value")?.text;
|
|
403
|
+
if (key && local)
|
|
404
|
+
bindings.push({ local, imported: key, source, kind: "named" });
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return bindings;
|
|
412
|
+
}
|
|
413
|
+
if (language === "go") {
|
|
414
|
+
const specs = [];
|
|
415
|
+
for (const decl of (0, tree_sitter_1.namedChildren)(root)) {
|
|
416
|
+
if (decl.type !== "import_declaration")
|
|
417
|
+
continue;
|
|
418
|
+
for (const child of (0, tree_sitter_1.namedChildren)(decl)) {
|
|
419
|
+
if (child.type === "import_spec")
|
|
420
|
+
specs.push(child);
|
|
421
|
+
else if (child.type === "import_spec_list") {
|
|
422
|
+
specs.push(...(0, tree_sitter_1.namedChildren)(child).filter((c) => c.type === "import_spec"));
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
for (const spec of specs) {
|
|
427
|
+
const pathNode = field(spec, "path");
|
|
428
|
+
if (!pathNode)
|
|
429
|
+
continue;
|
|
430
|
+
const source = stripQuotes(pathNode.text);
|
|
431
|
+
const alias = field(spec, "name")?.text;
|
|
432
|
+
if (alias === "_" || alias === ".")
|
|
433
|
+
continue;
|
|
434
|
+
const locals = alias ? [alias] : goPackageNameCandidates(source);
|
|
435
|
+
for (const local of locals) {
|
|
436
|
+
bindings.push({ local, imported: null, source, kind: "package" });
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return bindings;
|
|
440
|
+
}
|
|
441
|
+
if (language === "java") {
|
|
442
|
+
for (const decl of (0, tree_sitter_1.namedChildren)(root)) {
|
|
443
|
+
if (decl.type !== "import_declaration")
|
|
444
|
+
continue;
|
|
445
|
+
if (hasAnonymousChild(decl, "static"))
|
|
446
|
+
continue;
|
|
447
|
+
const scoped = firstChild(decl, "scoped_identifier", "identifier");
|
|
448
|
+
if (!scoped)
|
|
449
|
+
continue;
|
|
450
|
+
const source = scoped.text;
|
|
451
|
+
if (hasAnonymousChild(decl, "asterisk")) {
|
|
452
|
+
bindings.push({ local: "*", imported: null, source, kind: "wildcard" });
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
const last = source.split(".").pop() ?? source;
|
|
456
|
+
bindings.push({ local: last, imported: last, source, kind: "class" });
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return bindings;
|
|
460
|
+
}
|
|
461
|
+
if (language === "kotlin") {
|
|
462
|
+
// 当前语法:顶层 `import` 节点;兼容其他版本的 import_list / import_header 包装
|
|
463
|
+
const imports = [];
|
|
464
|
+
const collect = (node) => {
|
|
465
|
+
for (const c of (0, tree_sitter_1.namedChildren)(node)) {
|
|
466
|
+
if (c.type === "import" || c.type === "import_header")
|
|
467
|
+
imports.push(c);
|
|
468
|
+
else if (c.type === "import_list")
|
|
469
|
+
collect(c);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
collect(root);
|
|
473
|
+
for (const decl of imports) {
|
|
474
|
+
const qualified = firstChild(decl, "qualified_identifier", "identifier");
|
|
475
|
+
if (!qualified)
|
|
476
|
+
continue;
|
|
477
|
+
const source = qualified.text;
|
|
478
|
+
const wildcard = decl.children.some((c) => c !== null && !c.isNamed && c.type.includes("*"));
|
|
479
|
+
if (wildcard) {
|
|
480
|
+
bindings.push({ local: "*", imported: null, source, kind: "wildcard" });
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
// `import a.b.Util as U`:别名是紧随的 identifier(或 import_alias 包装)
|
|
484
|
+
const aliasNode = (0, tree_sitter_1.namedChildren)(decl).find((c) => c !== qualified && c.type === "identifier") ??
|
|
485
|
+
firstChild(decl, "import_alias");
|
|
486
|
+
const alias = aliasNode?.type === "import_alias" ? firstChild(aliasNode, "identifier")?.text : aliasNode?.text;
|
|
487
|
+
const last = source.split(".").pop() ?? source;
|
|
488
|
+
bindings.push({ local: alias ?? last, imported: last, source, kind: "class" });
|
|
489
|
+
}
|
|
490
|
+
return bindings;
|
|
491
|
+
}
|
|
492
|
+
if (language === "swift") {
|
|
493
|
+
// Swift 的 import 是模块级的,映射不到文件;只记录模块名供了解
|
|
494
|
+
for (const stmt of (0, tree_sitter_1.namedChildren)(root)) {
|
|
495
|
+
if (stmt.type !== "import_declaration")
|
|
496
|
+
continue;
|
|
497
|
+
const id = firstChild(stmt, "identifier");
|
|
498
|
+
if (!id)
|
|
499
|
+
continue;
|
|
500
|
+
const source = id.text;
|
|
501
|
+
const last = source.split(".").pop() ?? source;
|
|
502
|
+
bindings.push({ local: last, imported: null, source, kind: "package" });
|
|
503
|
+
}
|
|
504
|
+
return bindings;
|
|
505
|
+
}
|
|
506
|
+
return bindings;
|
|
507
|
+
}
|
|
508
|
+
/** Go 的 package 名 / Java 的 package 路径;TS 无此概念返回 null */
|
|
509
|
+
function packageName(root, language) {
|
|
510
|
+
if (language === "go") {
|
|
511
|
+
const clause = firstChild(root, "package_clause");
|
|
512
|
+
return clause ? firstChild(clause, "package_identifier")?.text ?? null : null;
|
|
513
|
+
}
|
|
514
|
+
if (language === "java") {
|
|
515
|
+
const decl = firstChild(root, "package_declaration");
|
|
516
|
+
return decl ? firstChild(decl, "scoped_identifier", "identifier")?.text ?? null : null;
|
|
517
|
+
}
|
|
518
|
+
if (language === "kotlin") {
|
|
519
|
+
const header = firstChild(root, "package_header");
|
|
520
|
+
return header ? firstChild(header, "qualified_identifier", "identifier")?.text ?? null : null;
|
|
521
|
+
}
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
const IDENTIFIER_TYPES = {
|
|
525
|
+
typescript: new Set(["identifier", "type_identifier", "property_identifier", "shorthand_property_identifier"]),
|
|
526
|
+
tsx: new Set(["identifier", "type_identifier", "property_identifier", "shorthand_property_identifier"]),
|
|
527
|
+
javascript: new Set(["identifier", "property_identifier", "shorthand_property_identifier"]),
|
|
528
|
+
go: new Set(["identifier", "type_identifier", "field_identifier", "package_identifier"]),
|
|
529
|
+
java: new Set(["identifier", "type_identifier"]),
|
|
530
|
+
kotlin: new Set(["identifier"]),
|
|
531
|
+
swift: new Set(["simple_identifier", "type_identifier"]),
|
|
532
|
+
};
|
|
533
|
+
/** 限定引用的父节点:[对象侧字段名, 成员侧字段名];字段名为 null 时按首/末子节点 */
|
|
534
|
+
const QUALIFIED_PARENTS = {
|
|
535
|
+
member_expression: ["object", "property"],
|
|
536
|
+
nested_type_identifier: ["module", "name"],
|
|
537
|
+
nested_identifier: [null, null],
|
|
538
|
+
selector_expression: ["operand", "field"],
|
|
539
|
+
qualified_type: ["package", "name"],
|
|
540
|
+
field_access: ["object", "field"],
|
|
541
|
+
method_invocation: ["object", "name"],
|
|
542
|
+
scoped_type_identifier: [null, null],
|
|
543
|
+
scoped_identifier: [null, null],
|
|
544
|
+
// Kotlin:`Foo.BAR` → (navigation_expression (identifier) (identifier)),无字段名
|
|
545
|
+
navigation_expression: [null, null],
|
|
546
|
+
};
|
|
547
|
+
/** 沿最右侧子节点下钻到叶子;叶子是标识符则返回其文本 */
|
|
548
|
+
function rightmostIdentifier(node, identifierTypes) {
|
|
549
|
+
let current = node;
|
|
550
|
+
for (;;) {
|
|
551
|
+
if (identifierTypes.has(current.type))
|
|
552
|
+
return current.text;
|
|
553
|
+
const kids = (0, tree_sitter_1.namedChildren)(current);
|
|
554
|
+
if (kids.length === 0)
|
|
555
|
+
return null;
|
|
556
|
+
current = kids[kids.length - 1];
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Swift:该标识符是否是某个 navigation_expression 目标的最右叶子、且后缀是标识符
|
|
561
|
+
* (`Foo.bar` 里的 Foo,或 `(a + Foo).bar` 里的 Foo)——这类对象侧引用由成员侧覆盖。
|
|
562
|
+
*/
|
|
563
|
+
function isSwiftNavigationBase(node, identifierTypes) {
|
|
564
|
+
let current = node;
|
|
565
|
+
while (current.parent) {
|
|
566
|
+
const parent = current.parent;
|
|
567
|
+
if (parent.type === "navigation_suffix")
|
|
568
|
+
return false;
|
|
569
|
+
if (parent.type === "navigation_expression") {
|
|
570
|
+
const target = field(parent, "target");
|
|
571
|
+
if (!target || !samePosition(target, current))
|
|
572
|
+
return false;
|
|
573
|
+
const suffix = field(parent, "suffix");
|
|
574
|
+
const suffixId = suffix ? (0, tree_sitter_1.namedChildren)(suffix)[0] : null;
|
|
575
|
+
return suffixId !== null && suffixId !== undefined && identifierTypes.has(suffixId.type);
|
|
576
|
+
}
|
|
577
|
+
const kids = (0, tree_sitter_1.namedChildren)(parent);
|
|
578
|
+
const last = kids[kids.length - 1];
|
|
579
|
+
if (!last || !samePosition(last, current))
|
|
580
|
+
return false;
|
|
581
|
+
current = parent;
|
|
582
|
+
}
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
/** 收集落在给定行(1-based)上的标识符引用;只下探与目标行相交的子树 */
|
|
586
|
+
function collectReferences(root, language, lines) {
|
|
587
|
+
if (lines.size === 0)
|
|
588
|
+
return [];
|
|
589
|
+
const rows = [...lines].map((l) => l - 1);
|
|
590
|
+
const minRow = Math.min(...rows);
|
|
591
|
+
const maxRow = Math.max(...rows);
|
|
592
|
+
const identifierTypes = IDENTIFIER_TYPES[language];
|
|
593
|
+
const seen = new Set();
|
|
594
|
+
const refs = [];
|
|
595
|
+
const push = (name, qualifier) => {
|
|
596
|
+
if (!name)
|
|
597
|
+
return;
|
|
598
|
+
const key = `${qualifier ?? ""}.${name}`;
|
|
599
|
+
if (seen.has(key))
|
|
600
|
+
return;
|
|
601
|
+
seen.add(key);
|
|
602
|
+
refs.push({ name, qualifier });
|
|
603
|
+
};
|
|
604
|
+
// 显式栈而不是递归:5000 层括号嵌套这种病态输入会撑爆调用栈,而异常一旦
|
|
605
|
+
// 逸出就会中断整次评审;先序、从左到右的顺序与递归版一致
|
|
606
|
+
const stack = [root];
|
|
607
|
+
while (stack.length > 0) {
|
|
608
|
+
const node = stack.pop();
|
|
609
|
+
if (node.endPosition.row < minRow || node.startPosition.row > maxRow)
|
|
610
|
+
continue;
|
|
611
|
+
const children = (0, tree_sitter_1.namedChildren)(node);
|
|
612
|
+
if (children.length > 0) {
|
|
613
|
+
for (let i = children.length - 1; i >= 0; i--)
|
|
614
|
+
stack.push(children[i]);
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
if (!identifierTypes.has(node.type) || !lines.has(node.startPosition.row + 1))
|
|
618
|
+
continue;
|
|
619
|
+
const parent = node.parent;
|
|
620
|
+
if (language === "swift") {
|
|
621
|
+
// `Foo.bar`:成员在 navigation_suffix 里,限定符是外层 navigation_expression 的 target。
|
|
622
|
+
// 该语法会把 `a + Foo.bar` 解析成 `(a + Foo).bar`,所以限定符取 target 的最右叶子标识符
|
|
623
|
+
if (parent?.type === "navigation_suffix") {
|
|
624
|
+
const target = parent.parent ? field(parent.parent, "target") : null;
|
|
625
|
+
push(node.text, target ? rightmostIdentifier(target, identifierTypes) : null);
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
// 对象侧(`Foo.bar` 里的 Foo,含上述被吞进二元表达式的形态)由成员侧那条限定引用覆盖
|
|
629
|
+
if (isSwiftNavigationBase(node, identifierTypes))
|
|
630
|
+
continue;
|
|
631
|
+
push(node.text, null);
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
const spec = parent ? QUALIFIED_PARENTS[parent.type] : undefined;
|
|
635
|
+
if (parent && spec) {
|
|
636
|
+
const siblings = (0, tree_sitter_1.namedChildren)(parent);
|
|
637
|
+
const object = spec[0] ? field(parent, spec[0]) : siblings[0] ?? null;
|
|
638
|
+
const member = spec[1] ? field(parent, spec[1]) : siblings[siblings.length - 1] ?? null;
|
|
639
|
+
if (member && samePosition(member, node) && object && identifierTypes.has(object.type)) {
|
|
640
|
+
push(node.text, object.text);
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
// 对象侧(`Foo.BAZ` 里的 Foo)由成员侧那条限定引用覆盖,不再单独产出
|
|
644
|
+
if (object && samePosition(object, node) && member && identifierTypes.has(member.type)) {
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
push(node.text, null);
|
|
649
|
+
}
|
|
650
|
+
return refs;
|
|
651
|
+
}
|
|
652
|
+
function declaratorNames(node) {
|
|
653
|
+
return (0, tree_sitter_1.namedChildren)(node)
|
|
654
|
+
.filter((c) => c.type === "identifier" || c.type === "type_identifier")
|
|
655
|
+
.map((c) => c.text);
|
|
656
|
+
}
|
|
657
|
+
/** TS 顶层语句里按名字找声明(含 export 包装、const 箭头函数、default 导出) */
|
|
658
|
+
function findTsTopLevel(root, language, name) {
|
|
659
|
+
for (const stmt of (0, tree_sitter_1.namedChildren)(root)) {
|
|
660
|
+
const isExport = stmt.type === "export_statement";
|
|
661
|
+
const target = isExport ? field(stmt, "declaration") ?? field(stmt, "value") : stmt;
|
|
662
|
+
if (!target)
|
|
663
|
+
continue;
|
|
664
|
+
if (name === "default") {
|
|
665
|
+
if (!isExport || !hasAnonymousChild(stmt, "default"))
|
|
666
|
+
continue;
|
|
667
|
+
if (target.type === "identifier")
|
|
668
|
+
return findTsTopLevel(root, language, target.text);
|
|
669
|
+
const decl = classifyNode(target, language);
|
|
670
|
+
return decl ?? makeDeclaration(stmt, "value", "default", null);
|
|
671
|
+
}
|
|
672
|
+
if (target.type === "lexical_declaration" || target.type === "variable_declaration") {
|
|
673
|
+
for (const declarator of (0, tree_sitter_1.namedChildren)(target)) {
|
|
674
|
+
if (declarator.type !== "variable_declarator")
|
|
675
|
+
continue;
|
|
676
|
+
if (field(declarator, "name")?.text !== name)
|
|
677
|
+
continue;
|
|
678
|
+
const value = field(declarator, "value");
|
|
679
|
+
if (value && (TS_FUNCTION_EXPRESSIONS.has(value.type) || value.type === "class")) {
|
|
680
|
+
return classifyNode(value, language) ?? makeDeclaration(liftExport(target), "value", name, null);
|
|
681
|
+
}
|
|
682
|
+
return makeDeclaration(liftExport(target), "value", name, null);
|
|
683
|
+
}
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
686
|
+
if (field(target, "name")?.text === name) {
|
|
687
|
+
const decl = classifyNode(target, language);
|
|
688
|
+
if (decl)
|
|
689
|
+
return decl;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
function findGoTopLevel(root, name) {
|
|
695
|
+
for (const stmt of (0, tree_sitter_1.namedChildren)(root)) {
|
|
696
|
+
if (stmt.type === "function_declaration" && field(stmt, "name")?.text === name) {
|
|
697
|
+
return classifyNode(stmt, "go");
|
|
698
|
+
}
|
|
699
|
+
if (stmt.type === "type_declaration") {
|
|
700
|
+
for (const spec of (0, tree_sitter_1.namedChildren)(stmt)) {
|
|
701
|
+
if ((spec.type === "type_spec" || spec.type === "type_alias") && field(spec, "name")?.text === name) {
|
|
702
|
+
const node = (0, tree_sitter_1.namedChildren)(stmt).length > 1 ? spec : stmt;
|
|
703
|
+
return makeDeclaration(node, "type", name, null);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
if (GO_VALUE.has(stmt.type)) {
|
|
709
|
+
for (const spec of (0, tree_sitter_1.namedChildren)(stmt)) {
|
|
710
|
+
if (spec.type !== "const_spec" && spec.type !== "var_spec")
|
|
711
|
+
continue;
|
|
712
|
+
if (declaratorNames(spec).includes(name)) {
|
|
713
|
+
return makeDeclaration(spec, "value", name, null);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return null;
|
|
719
|
+
}
|
|
720
|
+
function javaMemberName(member) {
|
|
721
|
+
if (member.type === "field_declaration") {
|
|
722
|
+
return (0, tree_sitter_1.namedChildren)(member)
|
|
723
|
+
.filter((c) => c.type === "variable_declarator")
|
|
724
|
+
.map((c) => field(c, "name")?.text ?? "")
|
|
725
|
+
.filter(Boolean);
|
|
726
|
+
}
|
|
727
|
+
if (member.type === "enum_constant") {
|
|
728
|
+
return [field(member, "name")?.text ?? (0, tree_sitter_1.namedChildren)(member)[0]?.text ?? ""].filter(Boolean);
|
|
729
|
+
}
|
|
730
|
+
const name = field(member, "name")?.text;
|
|
731
|
+
return name ? [name] : [];
|
|
732
|
+
}
|
|
733
|
+
function javaBodyMembers(body) {
|
|
734
|
+
const members = [];
|
|
735
|
+
for (const child of (0, tree_sitter_1.namedChildren)(body)) {
|
|
736
|
+
if (child.type === "enum_body_declarations")
|
|
737
|
+
members.push(...(0, tree_sitter_1.namedChildren)(child));
|
|
738
|
+
else
|
|
739
|
+
members.push(child);
|
|
740
|
+
}
|
|
741
|
+
return members;
|
|
742
|
+
}
|
|
743
|
+
/** 类成员的名字(一个成员可能声明多个名字:Java 字段列表、Kotlin 解构、Swift 多 case) */
|
|
744
|
+
function memberNames(member, language) {
|
|
745
|
+
if (language === "java")
|
|
746
|
+
return javaMemberName(member);
|
|
747
|
+
if (language === "kotlin") {
|
|
748
|
+
switch (member.type) {
|
|
749
|
+
case "property_declaration":
|
|
750
|
+
return kotlinPropertyNames(member);
|
|
751
|
+
case "enum_entry": {
|
|
752
|
+
const id = firstChild(member, "identifier");
|
|
753
|
+
return id ? [id.text] : [];
|
|
754
|
+
}
|
|
755
|
+
case "companion_object":
|
|
756
|
+
return [field(member, "name")?.text ?? "Companion"];
|
|
757
|
+
case "secondary_constructor":
|
|
758
|
+
return ["constructor"];
|
|
759
|
+
case "anonymous_initializer":
|
|
760
|
+
return ["init"];
|
|
761
|
+
case "type_alias": {
|
|
762
|
+
const name = field(member, "type")?.text ?? firstChild(member, "identifier")?.text;
|
|
763
|
+
return name ? [name] : [];
|
|
764
|
+
}
|
|
765
|
+
default: {
|
|
766
|
+
const name = field(member, "name")?.text;
|
|
767
|
+
return name ? [name] : [];
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (language === "swift") {
|
|
772
|
+
switch (member.type) {
|
|
773
|
+
case "property_declaration":
|
|
774
|
+
case "protocol_property_declaration":
|
|
775
|
+
return swiftPropertyNames(member);
|
|
776
|
+
case "enum_entry":
|
|
777
|
+
return (0, tree_sitter_1.namedChildren)(member)
|
|
778
|
+
.filter((c) => c.type === "simple_identifier")
|
|
779
|
+
.map((c) => c.text);
|
|
780
|
+
case "init_declaration":
|
|
781
|
+
return ["init"];
|
|
782
|
+
case "deinit_declaration":
|
|
783
|
+
return ["deinit"];
|
|
784
|
+
case "subscript_declaration":
|
|
785
|
+
return ["subscript"];
|
|
786
|
+
default: {
|
|
787
|
+
const name = field(member, "name")?.text;
|
|
788
|
+
return name ? [name] : [];
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return [field(member, "name")?.text ?? ""];
|
|
793
|
+
}
|
|
794
|
+
/** 类体里的成员列表;Kotlin 把 companion object 的成员一并展开(`Outer.member` 可直接引用) */
|
|
795
|
+
function bodyMembers(body, language) {
|
|
796
|
+
if (language === "java")
|
|
797
|
+
return javaBodyMembers(body);
|
|
798
|
+
if (language === "kotlin") {
|
|
799
|
+
const members = [];
|
|
800
|
+
for (const child of (0, tree_sitter_1.namedChildren)(body)) {
|
|
801
|
+
members.push(child);
|
|
802
|
+
if (child.type === "companion_object") {
|
|
803
|
+
const inner = firstChild(child, "class_body");
|
|
804
|
+
if (inner)
|
|
805
|
+
members.push(...(0, tree_sitter_1.namedChildren)(inner));
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return members;
|
|
809
|
+
}
|
|
810
|
+
return (0, tree_sitter_1.namedChildren)(body);
|
|
811
|
+
}
|
|
812
|
+
/** 类声明的类体节点:TS / Java / Swift 有 body 字段,Kotlin 靠子节点类型 */
|
|
813
|
+
function classBodyOf(decl) {
|
|
814
|
+
const classNode = decl.node.type === "export_statement" ? field(decl.node, "declaration") ?? decl.node : decl.node;
|
|
815
|
+
return (field(classNode, "body") ??
|
|
816
|
+
firstChild(classNode, "class_body", "enum_class_body", "protocol_body"));
|
|
817
|
+
}
|
|
818
|
+
function findJavaTopLevel(root, name) {
|
|
819
|
+
for (const stmt of (0, tree_sitter_1.namedChildren)(root)) {
|
|
820
|
+
if (JAVA_TOP_LEVEL_TYPES.has(stmt.type) && field(stmt, "name")?.text === name) {
|
|
821
|
+
return classifyNode(stmt, "java");
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
/** Kotlin 顶层:class / interface / object / fun / val / typealias 都按名字匹配 */
|
|
827
|
+
function findKotlinTopLevel(root, name) {
|
|
828
|
+
for (const stmt of (0, tree_sitter_1.namedChildren)(root)) {
|
|
829
|
+
if (isCommentNode(stmt) || !memberNames(stmt, "kotlin").includes(name))
|
|
830
|
+
continue;
|
|
831
|
+
return classifyNode(stmt, "kotlin") ?? makeDeclaration(stmt, "value", name, null);
|
|
832
|
+
}
|
|
833
|
+
return null;
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Swift 顶层:同一文件可能同时有主声明与若干 extension;有成员名时优先含该成员的声明,
|
|
837
|
+
* 否则主声明优先于 extension。
|
|
838
|
+
*/
|
|
839
|
+
function findSwiftTopLevel(root, name, member) {
|
|
840
|
+
let primary = null;
|
|
841
|
+
let extension = null;
|
|
842
|
+
for (const stmt of (0, tree_sitter_1.namedChildren)(root)) {
|
|
843
|
+
if (isCommentNode(stmt) || !memberNames(stmt, "swift").includes(name))
|
|
844
|
+
continue;
|
|
845
|
+
const decl = classifyNode(stmt, "swift") ?? makeDeclaration(stmt, "value", name, null);
|
|
846
|
+
if (member && decl.kind === "class" && memberOf(decl, "swift", member))
|
|
847
|
+
return decl;
|
|
848
|
+
const isExtension = stmt.type === "class_declaration" && anonymousChildren(stmt).includes("extension");
|
|
849
|
+
if (isExtension) {
|
|
850
|
+
if (!extension)
|
|
851
|
+
extension = decl;
|
|
852
|
+
}
|
|
853
|
+
else if (!primary) {
|
|
854
|
+
primary = decl;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return primary ?? extension;
|
|
858
|
+
}
|
|
859
|
+
function memberOf(decl, language, member) {
|
|
860
|
+
const classBody = classBodyOf(decl);
|
|
861
|
+
if (!classBody)
|
|
862
|
+
return null;
|
|
863
|
+
for (const m of bodyMembers(classBody, language)) {
|
|
864
|
+
if (isCommentNode(m))
|
|
865
|
+
continue;
|
|
866
|
+
if (!memberNames(m, language).includes(member))
|
|
867
|
+
continue;
|
|
868
|
+
const classified = classifyNode(m, language);
|
|
869
|
+
if (classified)
|
|
870
|
+
return classified;
|
|
871
|
+
return makeDeclaration(m, "value", member, null);
|
|
872
|
+
}
|
|
873
|
+
return null;
|
|
874
|
+
}
|
|
875
|
+
/** 在目标文件的语法树里按名字找顶层声明;member 用于 `Foo.BAR` 这类类成员引用 */
|
|
876
|
+
function findDeclaration(root, language, name, member) {
|
|
877
|
+
let decl = null;
|
|
878
|
+
if (isTsLike(language))
|
|
879
|
+
decl = findTsTopLevel(root, language, name);
|
|
880
|
+
else if (language === "go")
|
|
881
|
+
decl = findGoTopLevel(root, name);
|
|
882
|
+
else if (language === "java")
|
|
883
|
+
decl = findJavaTopLevel(root, name);
|
|
884
|
+
else if (language === "kotlin")
|
|
885
|
+
decl = findKotlinTopLevel(root, name);
|
|
886
|
+
else
|
|
887
|
+
decl = findSwiftTopLevel(root, name, member);
|
|
888
|
+
if (!decl)
|
|
889
|
+
return null;
|
|
890
|
+
const memberDecl = member && decl.kind === "class" ? memberOf(decl, language, member) : null;
|
|
891
|
+
return { decl, member: memberDecl };
|
|
892
|
+
}
|
|
893
|
+
function isPublicMember(member, language) {
|
|
894
|
+
if (language === "java") {
|
|
895
|
+
if (member.type === "enum_constant")
|
|
896
|
+
return true;
|
|
897
|
+
const modifiers = firstChild(member, "modifiers")?.text ?? "";
|
|
898
|
+
if (/\bprivate\b|\bprotected\b/.test(modifiers))
|
|
899
|
+
return false;
|
|
900
|
+
return /\bpublic\b/.test(modifiers) || modifiers === "";
|
|
901
|
+
}
|
|
902
|
+
if (language === "kotlin" || language === "swift") {
|
|
903
|
+
if (member.type === "enum_entry")
|
|
904
|
+
return true;
|
|
905
|
+
const modifiers = firstChild(member, "modifiers");
|
|
906
|
+
if (!modifiers)
|
|
907
|
+
return true;
|
|
908
|
+
for (const mod of (0, tree_sitter_1.namedChildren)(modifiers)) {
|
|
909
|
+
if (mod.type !== "visibility_modifier")
|
|
910
|
+
continue;
|
|
911
|
+
// Swift 的 `private(set)` 只限制写入,读仍是公开的
|
|
912
|
+
const text = mod.text.replace(/\s+/g, "");
|
|
913
|
+
if (language === "kotlin" && /^(private|protected)$/.test(text))
|
|
914
|
+
return false;
|
|
915
|
+
if (language === "swift" && /^(private|fileprivate)$/.test(text))
|
|
916
|
+
return false;
|
|
917
|
+
}
|
|
918
|
+
return true;
|
|
919
|
+
}
|
|
920
|
+
const accessibility = member.children.find((c) => c !== null && c.type === "accessibility_modifier");
|
|
921
|
+
if (accessibility && /private|protected/.test(accessibility.text))
|
|
922
|
+
return false;
|
|
923
|
+
const name = field(member, "name")?.text ?? "";
|
|
924
|
+
return !name.startsWith("#");
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* 决定渲染哪些行(1-based 闭区间,已排序不重叠):
|
|
928
|
+
* 函数 → 签名;类型/值 → 整体但封顶 MAX_TYPE_LINES;
|
|
929
|
+
* 类 → 类头 + 被引用成员的签名(无具体成员时列公开成员签名),封顶 MAX_TYPE_LINES。
|
|
930
|
+
*/
|
|
931
|
+
function renderRanges(found, language) {
|
|
932
|
+
const { decl, member } = found;
|
|
933
|
+
if (decl.kind === "function")
|
|
934
|
+
return [[decl.startLine, decl.headerEndLine]];
|
|
935
|
+
if (decl.kind !== "class") {
|
|
936
|
+
return [[decl.startLine, Math.min(decl.endLine, decl.startLine + exports.MAX_TYPE_LINES - 1)]];
|
|
937
|
+
}
|
|
938
|
+
const ranges = [[decl.startLine, decl.headerEndLine]];
|
|
939
|
+
let used = decl.headerEndLine - decl.startLine + 1;
|
|
940
|
+
const pushRange = (start, end) => {
|
|
941
|
+
const last = ranges[ranges.length - 1];
|
|
942
|
+
if (start <= last[1])
|
|
943
|
+
return; // 已在类头范围内
|
|
944
|
+
const capped = Math.min(end, start + Math.max(0, exports.MAX_TYPE_LINES - used) - 1);
|
|
945
|
+
if (capped < start)
|
|
946
|
+
return;
|
|
947
|
+
ranges.push([start, capped]);
|
|
948
|
+
used += capped - start + 1;
|
|
949
|
+
};
|
|
950
|
+
if (member) {
|
|
951
|
+
pushRange(member.startLine, member.headerEndLine);
|
|
952
|
+
return ranges;
|
|
953
|
+
}
|
|
954
|
+
const body = classBodyOf(decl);
|
|
955
|
+
if (!body)
|
|
956
|
+
return ranges;
|
|
957
|
+
for (const m of bodyMembers(body, language)) {
|
|
958
|
+
if (used >= exports.MAX_TYPE_LINES)
|
|
959
|
+
break;
|
|
960
|
+
if (isCommentNode(m))
|
|
961
|
+
continue;
|
|
962
|
+
if (!isPublicMember(m, language))
|
|
963
|
+
continue;
|
|
964
|
+
const classified = classifyNode(m, language);
|
|
965
|
+
const end = classified ? classified.headerEndLine : Math.min((0, tree_sitter_1.endLine)(m), (0, tree_sitter_1.startLine)(m));
|
|
966
|
+
pushRange((0, tree_sitter_1.startLine)(m), end);
|
|
967
|
+
}
|
|
968
|
+
return ranges;
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* 语法树不可用(超大文件解析超时、运行时缺失)时的兜底:按行正则找声明头。
|
|
972
|
+
* 只认「行首就是声明」的常见形态,找不到就返回空。
|
|
973
|
+
*/
|
|
974
|
+
function findDeclarationByRegex(lines, language, name) {
|
|
975
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
976
|
+
const patterns = isTsLike(language)
|
|
977
|
+
? [
|
|
978
|
+
new RegExp(`^\\s*(export\\s+)?(default\\s+)?(declare\\s+)?(abstract\\s+)?(async\\s+)?(function\\*?\\s+${escaped}\\b|class\\s+${escaped}\\b|interface\\s+${escaped}\\b|type\\s+${escaped}\\b|enum\\s+${escaped}\\b|(const|let|var)\\s+${escaped}\\b)`),
|
|
979
|
+
]
|
|
980
|
+
: language === "go"
|
|
981
|
+
? [
|
|
982
|
+
new RegExp(`^\\s*func\\s+(\\([^)]*\\)\\s*)?${escaped}\\s*[\\[(]`),
|
|
983
|
+
new RegExp(`^\\s*type\\s+${escaped}\\b`),
|
|
984
|
+
new RegExp(`^\\s*(const|var)\\s+${escaped}\\b`),
|
|
985
|
+
new RegExp(`^\\s*${escaped}\\b[^=]*=`),
|
|
986
|
+
]
|
|
987
|
+
: language === "java"
|
|
988
|
+
? [
|
|
989
|
+
new RegExp(`^\\s*((public|private|protected|static|final|abstract|sealed|non-sealed)\\s+)*(class|interface|enum|record|@interface)\\s+${escaped}\\b`),
|
|
990
|
+
]
|
|
991
|
+
: language === "kotlin"
|
|
992
|
+
? [
|
|
993
|
+
// 修饰符 / 注解前缀宽松匹配;fun 可带类型参数与接收者(`fun <T> List<T>.foo`)
|
|
994
|
+
new RegExp(`^\\s*(?:[\\w@:.()"']+\\s+)*(?:class|interface|object|typealias|val|var|fun)(?:\\s*<[^>]*>)?(?:\\s+[\\w.<>?]+\\.)?\\s*\`?${escaped}\`?\\b`),
|
|
995
|
+
]
|
|
996
|
+
: [
|
|
997
|
+
// swift
|
|
998
|
+
new RegExp(`^\\s*(?:@\\w+(?:\\([^)]*\\))?\\s+)*(?:(?:public|private|fileprivate|internal|open|final|indirect|static|class|dynamic|override|mutating|lazy|weak|unowned|nonisolated|package|convenience|required)(?:\\([^)]*\\))?\\s+)*(?:class|struct|enum|protocol|actor|typealias|extension|func|let|var|case|init)\\s+${escaped}\\b`),
|
|
999
|
+
];
|
|
1000
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1001
|
+
const line = lines[i];
|
|
1002
|
+
if (!line.includes(name))
|
|
1003
|
+
continue;
|
|
1004
|
+
if (!patterns.some((p) => p.test(line)))
|
|
1005
|
+
continue;
|
|
1006
|
+
let end = i;
|
|
1007
|
+
if (!/[{;=]/.test(line)) {
|
|
1008
|
+
for (let j = i + 1; j < Math.min(lines.length, i + 5); j++) {
|
|
1009
|
+
end = j;
|
|
1010
|
+
if (/[{;]/.test(lines[j]))
|
|
1011
|
+
break;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
return [[i + 1, end + 1]];
|
|
1015
|
+
}
|
|
1016
|
+
return [];
|
|
1017
|
+
}
|
|
1018
|
+
//# sourceMappingURL=symbols.js.map
|