@wuyanbin/ai-code-review-cli-remote 1.1.0 → 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.
@@ -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
- // java
145
- if (JAVA_FUNCTION.has(node.type)) {
146
- return makeDeclaration(node, "function", field(node, "name")?.text ?? null, field(node, "body"));
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 (JAVA_CLASS_LIKE.has(node.type)) {
149
- return makeDeclaration(node, "class", field(node, "name")?.text ?? null, field(node, "body"));
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 (JAVA_TYPE_LIKE.has(node.type)) {
152
- return makeDeclaration(node, "type", field(node, "name")?.text ?? null, null);
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
- for (const decl of (0, tree_sitter_1.namedChildren)(root)) {
293
- if (decl.type !== "import_declaration")
294
- continue;
295
- if (hasAnonymousChild(decl, "static"))
296
- continue;
297
- const scoped = firstChild(decl, "scoped_identifier", "identifier");
298
- if (!scoped)
299
- continue;
300
- const source = scoped.text;
301
- if (hasAnonymousChild(decl, "asterisk")) {
302
- bindings.push({ local: "*", imported: null, source, kind: "wildcard" });
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
- else {
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: last, source, kind: "class" });
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 body = field(decl.node, "body") ?? (decl.node.type === "export_statement" ? null : null);
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 members = language === "java" ? javaBodyMembers(classBody) : (0, tree_sitter_1.namedChildren)(classBody);
501
- for (const m of members) {
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
- const names = language === "java" ? javaMemberName(m) : [field(m, "name")?.text ?? ""];
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 classNode = decl.node.type === "export_statement" ? field(decl.node, "declaration") ?? decl.node : decl.node;
572
- const body = field(classNode, "body");
954
+ const body = classBodyOf(decl);
573
955
  if (!body)
574
956
  return ranges;
575
- const members = language === "java" ? javaBodyMembers(body) : (0, tree_sitter_1.namedChildren)(body);
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.type === "comment" || m.type === "line_comment" || m.type === "block_comment")
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
- new RegExp(`^\\s*((public|private|protected|static|final|abstract|sealed|non-sealed)\\s+)*(class|interface|enum|record|@interface)\\s+${escaped}\\b`),
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))