@wuyanbin/ai-code-review-cli-remote 1.1.0 → 1.3.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/confidence.d.ts +40 -0
- package/dist/core/confidence.d.ts.map +1 -0
- package/dist/core/confidence.js +74 -0
- package/dist/core/confidence.js.map +1 -0
- package/dist/core/context.js +1 -1
- package/dist/core/context.js.map +1 -1
- package/dist/core/diff.d.ts +5 -0
- package/dist/core/diff.d.ts.map +1 -1
- package/dist/core/diff.js +24 -0
- package/dist/core/diff.js.map +1 -1
- package/dist/core/import-resolve.d.ts +20 -9
- package/dist/core/import-resolve.d.ts.map +1 -1
- package/dist/core/import-resolve.js +96 -15
- package/dist/core/import-resolve.js.map +1 -1
- package/dist/core/incremental.d.ts.map +1 -1
- package/dist/core/incremental.js +6 -0
- package/dist/core/incremental.js.map +1 -1
- 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 +22 -4
- 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 +4 -0
- package/dist/core/symbol-context.d.ts.map +1 -1
- package/dist/core/symbol-context.js +98 -14
- package/dist/core/symbol-context.js.map +1 -1
- package/dist/core/symbols.d.ts +3 -3
- package/dist/core/symbols.d.ts.map +1 -1
- package/dist/core/symbols.js +428 -37
- package/dist/core/symbols.js.map +1 -1
- package/dist/core/tree-sitter.d.ts +5 -1
- package/dist/core/tree-sitter.d.ts.map +1 -1
- package/dist/core/tree-sitter.js +10 -1
- package/dist/core/tree-sitter.js.map +1 -1
- package/dist/gitlab/api.d.ts.map +1 -1
- package/dist/gitlab/api.js +8 -1
- package/dist/gitlab/api.js.map +1 -1
- package/dist/platform/findings.d.ts.map +1 -1
- package/dist/platform/findings.js +3 -0
- package/dist/platform/findings.js.map +1 -1
- package/dist/tree-sitter/tree-sitter-kotlin.wasm +0 -0
- package/dist/tree-sitter/tree-sitter-swift.wasm +0 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/core/symbols.js
CHANGED
|
@@ -85,6 +85,75 @@ const JAVA_TYPE_LIKE = new Set([
|
|
|
85
85
|
]);
|
|
86
86
|
/** 顶层按名字查找时,class 与 type 两类都算「类型声明」 */
|
|
87
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
|
+
}
|
|
88
157
|
function isTsLike(language) {
|
|
89
158
|
return (language === "typescript" || language === "tsx" || language === "javascript");
|
|
90
159
|
}
|
|
@@ -141,15 +210,95 @@ function classifyNode(node, language) {
|
|
|
141
210
|
}
|
|
142
211
|
return null;
|
|
143
212
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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;
|
|
147
224
|
}
|
|
148
|
-
if (
|
|
149
|
-
|
|
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;
|
|
150
270
|
}
|
|
151
|
-
if (
|
|
152
|
-
|
|
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;
|
|
153
302
|
}
|
|
154
303
|
return null;
|
|
155
304
|
}
|
|
@@ -289,22 +438,70 @@ function collectImports(root, language) {
|
|
|
289
438
|
}
|
|
290
439
|
return bindings;
|
|
291
440
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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" });
|
|
303
489
|
}
|
|
304
|
-
|
|
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;
|
|
305
501
|
const last = source.split(".").pop() ?? source;
|
|
306
|
-
bindings.push({ local: last, imported:
|
|
502
|
+
bindings.push({ local: last, imported: null, source, kind: "package" });
|
|
307
503
|
}
|
|
504
|
+
return bindings;
|
|
308
505
|
}
|
|
309
506
|
return bindings;
|
|
310
507
|
}
|
|
@@ -318,6 +515,10 @@ function packageName(root, language) {
|
|
|
318
515
|
const decl = firstChild(root, "package_declaration");
|
|
319
516
|
return decl ? firstChild(decl, "scoped_identifier", "identifier")?.text ?? null : null;
|
|
320
517
|
}
|
|
518
|
+
if (language === "kotlin") {
|
|
519
|
+
const header = firstChild(root, "package_header");
|
|
520
|
+
return header ? firstChild(header, "qualified_identifier", "identifier")?.text ?? null : null;
|
|
521
|
+
}
|
|
321
522
|
return null;
|
|
322
523
|
}
|
|
323
524
|
const IDENTIFIER_TYPES = {
|
|
@@ -326,6 +527,8 @@ const IDENTIFIER_TYPES = {
|
|
|
326
527
|
javascript: new Set(["identifier", "property_identifier", "shorthand_property_identifier"]),
|
|
327
528
|
go: new Set(["identifier", "type_identifier", "field_identifier", "package_identifier"]),
|
|
328
529
|
java: new Set(["identifier", "type_identifier"]),
|
|
530
|
+
kotlin: new Set(["identifier"]),
|
|
531
|
+
swift: new Set(["simple_identifier", "type_identifier"]),
|
|
329
532
|
};
|
|
330
533
|
/** 限定引用的父节点:[对象侧字段名, 成员侧字段名];字段名为 null 时按首/末子节点 */
|
|
331
534
|
const QUALIFIED_PARENTS = {
|
|
@@ -338,7 +541,47 @@ const QUALIFIED_PARENTS = {
|
|
|
338
541
|
method_invocation: ["object", "name"],
|
|
339
542
|
scoped_type_identifier: [null, null],
|
|
340
543
|
scoped_identifier: [null, null],
|
|
544
|
+
// Kotlin:`Foo.BAR` → (navigation_expression (identifier) (identifier)),无字段名
|
|
545
|
+
navigation_expression: [null, null],
|
|
341
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
|
+
}
|
|
342
585
|
/** 收集落在给定行(1-based)上的标识符引用;只下探与目标行相交的子树 */
|
|
343
586
|
function collectReferences(root, language, lines) {
|
|
344
587
|
if (lines.size === 0)
|
|
@@ -374,6 +617,20 @@ function collectReferences(root, language, lines) {
|
|
|
374
617
|
if (!identifierTypes.has(node.type) || !lines.has(node.startPosition.row + 1))
|
|
375
618
|
continue;
|
|
376
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
|
+
}
|
|
377
634
|
const spec = parent ? QUALIFIED_PARENTS[parent.type] : undefined;
|
|
378
635
|
if (parent && spec) {
|
|
379
636
|
const siblings = (0, tree_sitter_1.namedChildren)(parent);
|
|
@@ -483,6 +740,81 @@ function javaBodyMembers(body) {
|
|
|
483
740
|
}
|
|
484
741
|
return members;
|
|
485
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
|
+
}
|
|
486
818
|
function findJavaTopLevel(root, name) {
|
|
487
819
|
for (const stmt of (0, tree_sitter_1.namedChildren)(root)) {
|
|
488
820
|
if (JAVA_TOP_LEVEL_TYPES.has(stmt.type) && field(stmt, "name")?.text === name) {
|
|
@@ -491,18 +823,47 @@ function findJavaTopLevel(root, name) {
|
|
|
491
823
|
}
|
|
492
824
|
return null;
|
|
493
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
|
+
}
|
|
494
859
|
function memberOf(decl, language, member) {
|
|
495
|
-
const
|
|
496
|
-
const classNode = decl.node.type === "export_statement" ? field(decl.node, "declaration") ?? decl.node : decl.node;
|
|
497
|
-
const classBody = body ?? field(classNode, "body");
|
|
860
|
+
const classBody = classBodyOf(decl);
|
|
498
861
|
if (!classBody)
|
|
499
862
|
return null;
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
if (m.type === "comment" || m.type === "line_comment" || m.type === "block_comment")
|
|
863
|
+
for (const m of bodyMembers(classBody, language)) {
|
|
864
|
+
if (isCommentNode(m))
|
|
503
865
|
continue;
|
|
504
|
-
|
|
505
|
-
if (!names.includes(member))
|
|
866
|
+
if (!memberNames(m, language).includes(member))
|
|
506
867
|
continue;
|
|
507
868
|
const classified = classifyNode(m, language);
|
|
508
869
|
if (classified)
|
|
@@ -518,8 +879,12 @@ function findDeclaration(root, language, name, member) {
|
|
|
518
879
|
decl = findTsTopLevel(root, language, name);
|
|
519
880
|
else if (language === "go")
|
|
520
881
|
decl = findGoTopLevel(root, name);
|
|
521
|
-
else
|
|
882
|
+
else if (language === "java")
|
|
522
883
|
decl = findJavaTopLevel(root, name);
|
|
884
|
+
else if (language === "kotlin")
|
|
885
|
+
decl = findKotlinTopLevel(root, name);
|
|
886
|
+
else
|
|
887
|
+
decl = findSwiftTopLevel(root, name, member);
|
|
523
888
|
if (!decl)
|
|
524
889
|
return null;
|
|
525
890
|
const memberDecl = member && decl.kind === "class" ? memberOf(decl, language, member) : null;
|
|
@@ -534,6 +899,24 @@ function isPublicMember(member, language) {
|
|
|
534
899
|
return false;
|
|
535
900
|
return /\bpublic\b/.test(modifiers) || modifiers === "";
|
|
536
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
|
+
}
|
|
537
920
|
const accessibility = member.children.find((c) => c !== null && c.type === "accessibility_modifier");
|
|
538
921
|
if (accessibility && /private|protected/.test(accessibility.text))
|
|
539
922
|
return false;
|
|
@@ -568,15 +951,13 @@ function renderRanges(found, language) {
|
|
|
568
951
|
pushRange(member.startLine, member.headerEndLine);
|
|
569
952
|
return ranges;
|
|
570
953
|
}
|
|
571
|
-
const
|
|
572
|
-
const body = field(classNode, "body");
|
|
954
|
+
const body = classBodyOf(decl);
|
|
573
955
|
if (!body)
|
|
574
956
|
return ranges;
|
|
575
|
-
const
|
|
576
|
-
for (const m of members) {
|
|
957
|
+
for (const m of bodyMembers(body, language)) {
|
|
577
958
|
if (used >= exports.MAX_TYPE_LINES)
|
|
578
959
|
break;
|
|
579
|
-
if (m
|
|
960
|
+
if (isCommentNode(m))
|
|
580
961
|
continue;
|
|
581
962
|
if (!isPublicMember(m, language))
|
|
582
963
|
continue;
|
|
@@ -603,9 +984,19 @@ function findDeclarationByRegex(lines, language, name) {
|
|
|
603
984
|
new RegExp(`^\\s*(const|var)\\s+${escaped}\\b`),
|
|
604
985
|
new RegExp(`^\\s*${escaped}\\b[^=]*=`),
|
|
605
986
|
]
|
|
606
|
-
:
|
|
607
|
-
|
|
608
|
-
|
|
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
|
+
];
|
|
609
1000
|
for (let i = 0; i < lines.length; i++) {
|
|
610
1001
|
const line = lines[i];
|
|
611
1002
|
if (!line.includes(name))
|