clast-ai 0.1.2 → 0.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.
Files changed (290) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/.claude-plugin/plugin.json +11 -9
  3. package/README.md +447 -401
  4. package/bench/ab/onair-concept.json +22 -0
  5. package/bench/ab/onair-deadcode.json +7 -0
  6. package/bench/ab/onair-hard.json +32 -0
  7. package/bench/ab/onair-impact.json +17 -0
  8. package/bench/ab/onair-refactors.json +18 -0
  9. package/bench/ab/onair-survey.json +17 -0
  10. package/bench/ab/onair-tasks.json +20 -0
  11. package/bench/ab/onair.json +52 -0
  12. package/bench/ab/vscode-concept.json +22 -0
  13. package/bench/ab/vscode-hard.json +32 -0
  14. package/bench/ab/vscode-impact.json +17 -0
  15. package/bench/ab/vscode-refactors.json +18 -0
  16. package/bench/ab/vscode-tasks.json +20 -0
  17. package/bench/ab/vscode.json +52 -0
  18. package/bench/ab.mjs +213 -0
  19. package/bench/bench.mjs +98 -0
  20. package/bench/resolution.mjs +75 -0
  21. package/dist/cli.d.ts +2 -0
  22. package/dist/cli.js +257 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/config/defaults.d.ts +12 -2
  25. package/dist/config/defaults.js +38 -31
  26. package/dist/config/defaults.js.map +1 -1
  27. package/dist/config/index.d.ts +1 -1
  28. package/dist/config/index.js +26 -14
  29. package/dist/config/index.js.map +1 -1
  30. package/dist/db/index.d.ts +73 -13
  31. package/dist/db/index.js +481 -39
  32. package/dist/db/index.js.map +1 -1
  33. package/dist/engine.d.ts +15 -0
  34. package/dist/engine.js +28 -0
  35. package/dist/engine.js.map +1 -0
  36. package/dist/indexer/imports.d.ts +6 -0
  37. package/dist/indexer/imports.js +254 -0
  38. package/dist/indexer/imports.js.map +1 -0
  39. package/dist/indexer/index.d.ts +63 -0
  40. package/dist/indexer/index.js +360 -0
  41. package/dist/indexer/index.js.map +1 -0
  42. package/dist/indexer/process.d.ts +22 -0
  43. package/dist/indexer/process.js +42 -0
  44. package/dist/indexer/process.js.map +1 -0
  45. package/dist/indexer/scan.d.ts +43 -0
  46. package/dist/indexer/scan.js +223 -0
  47. package/dist/indexer/scan.js.map +1 -0
  48. package/dist/indexer/worker.d.ts +1 -0
  49. package/dist/indexer/worker.js +23 -0
  50. package/dist/indexer/worker.js.map +1 -0
  51. package/dist/llm/comment-generator.d.ts +17 -19
  52. package/dist/llm/comment-generator.js +46 -80
  53. package/dist/llm/comment-generator.js.map +1 -1
  54. package/dist/llm/index.d.ts +0 -1
  55. package/dist/llm/index.js.map +1 -1
  56. package/dist/llm/prompts.d.ts +0 -1
  57. package/dist/llm/prompts.js +16 -16
  58. package/dist/parser/extractor.d.ts +21 -16
  59. package/dist/parser/extractor.js +700 -228
  60. package/dist/parser/extractor.js.map +1 -1
  61. package/dist/parser/index.d.ts +17 -21
  62. package/dist/parser/index.js +115 -171
  63. package/dist/parser/index.js.map +1 -1
  64. package/dist/parser/langs/c.d.ts +3 -0
  65. package/dist/parser/langs/c.js +879 -0
  66. package/dist/parser/langs/c.js.map +1 -0
  67. package/dist/parser/langs/csharp.d.ts +2 -0
  68. package/dist/parser/langs/csharp.js +548 -0
  69. package/dist/parser/langs/csharp.js.map +1 -0
  70. package/dist/parser/langs/dart.d.ts +2 -0
  71. package/dist/parser/langs/dart.js +893 -0
  72. package/dist/parser/langs/dart.js.map +1 -0
  73. package/dist/parser/langs/go.d.ts +2 -0
  74. package/dist/parser/langs/go.js +455 -0
  75. package/dist/parser/langs/go.js.map +1 -0
  76. package/dist/parser/langs/java.d.ts +2 -0
  77. package/dist/parser/langs/java.js +390 -0
  78. package/dist/parser/langs/java.js.map +1 -0
  79. package/dist/parser/langs/php.d.ts +2 -0
  80. package/dist/parser/langs/php.js +486 -0
  81. package/dist/parser/langs/php.js.map +1 -0
  82. package/dist/parser/langs/python.d.ts +2 -0
  83. package/dist/parser/langs/python.js +216 -0
  84. package/dist/parser/langs/python.js.map +1 -0
  85. package/dist/parser/langs/ruby.d.ts +2 -0
  86. package/dist/parser/langs/ruby.js +602 -0
  87. package/dist/parser/langs/ruby.js.map +1 -0
  88. package/dist/parser/langs/rust.d.ts +2 -0
  89. package/dist/parser/langs/rust.js +931 -0
  90. package/dist/parser/langs/rust.js.map +1 -0
  91. package/dist/parser/langs/swift.d.ts +2 -0
  92. package/dist/parser/langs/swift.js +616 -0
  93. package/dist/parser/langs/swift.js.map +1 -0
  94. package/dist/parser/langs/typescript.d.ts +4 -0
  95. package/dist/parser/langs/typescript.js +535 -0
  96. package/dist/parser/langs/typescript.js.map +1 -0
  97. package/dist/parser/registry.d.ts +16 -0
  98. package/dist/parser/registry.js +87 -0
  99. package/dist/parser/registry.js.map +1 -0
  100. package/dist/parser/returns.d.ts +9 -0
  101. package/dist/parser/returns.js +152 -0
  102. package/dist/parser/returns.js.map +1 -0
  103. package/dist/parser/spec.d.ts +165 -0
  104. package/dist/parser/spec.js +188 -0
  105. package/dist/parser/spec.js.map +1 -0
  106. package/dist/query/arity.d.ts +27 -0
  107. package/dist/query/arity.js +187 -0
  108. package/dist/query/arity.js.map +1 -0
  109. package/dist/query/callgraph.d.ts +113 -0
  110. package/dist/query/callgraph.js +332 -0
  111. package/dist/query/callgraph.js.map +1 -0
  112. package/dist/query/format.d.ts +36 -0
  113. package/dist/query/format.js +136 -0
  114. package/dist/query/format.js.map +1 -0
  115. package/dist/query/hierarchy.d.ts +44 -0
  116. package/dist/query/hierarchy.js +441 -0
  117. package/dist/query/hierarchy.js.map +1 -0
  118. package/dist/query/resolve.d.ts +265 -0
  119. package/dist/query/resolve.js +1957 -0
  120. package/dist/query/resolve.js.map +1 -0
  121. package/dist/query/search.d.ts +35 -0
  122. package/dist/query/search.js +379 -0
  123. package/dist/query/search.js.map +1 -0
  124. package/dist/query/words.d.ts +30 -0
  125. package/dist/query/words.js +201 -0
  126. package/dist/query/words.js.map +1 -0
  127. package/dist/server/index.d.ts +1 -3
  128. package/dist/server/index.js +313 -73
  129. package/dist/server/index.js.map +1 -1
  130. package/dist/tools/context.d.ts +35 -0
  131. package/dist/tools/context.js +79 -0
  132. package/dist/tools/context.js.map +1 -0
  133. package/dist/tools/explore.d.ts +51 -0
  134. package/dist/tools/explore.js +779 -0
  135. package/dist/tools/explore.js.map +1 -0
  136. package/dist/tools/impact.d.ts +21 -0
  137. package/dist/tools/impact.js +743 -0
  138. package/dist/tools/impact.js.map +1 -0
  139. package/dist/tools/search.d.ts +59 -0
  140. package/dist/tools/search.js +843 -0
  141. package/dist/tools/search.js.map +1 -0
  142. package/dist/tools/stats.d.ts +26 -0
  143. package/dist/tools/stats.js +604 -0
  144. package/dist/tools/stats.js.map +1 -0
  145. package/dist/types.d.ts +95 -40
  146. package/dist/types.js +9 -1
  147. package/dist/types.js.map +1 -1
  148. package/dist/version.d.ts +2 -0
  149. package/dist/version.js +3 -0
  150. package/dist/version.js.map +1 -0
  151. package/dist/watcher/index.d.ts +18 -16
  152. package/dist/watcher/index.js +94 -77
  153. package/dist/watcher/index.js.map +1 -1
  154. package/package.json +21 -24
  155. package/plugin/server.mjs +50 -0
  156. package/skills/audit/SKILL.md +37 -0
  157. package/skills/deadcode/SKILL.md +33 -0
  158. package/skills/explore/SKILL.md +54 -0
  159. package/skills/impact/SKILL.md +24 -0
  160. package/skills/status/SKILL.md +11 -0
  161. package/src/cli.ts +256 -0
  162. package/src/config/defaults.ts +50 -32
  163. package/src/config/index.ts +23 -14
  164. package/src/db/index.ts +540 -43
  165. package/src/engine.ts +30 -0
  166. package/src/indexer/imports.ts +241 -0
  167. package/src/indexer/index.ts +369 -0
  168. package/src/indexer/process.ts +63 -0
  169. package/src/indexer/scan.ts +226 -0
  170. package/src/indexer/worker.ts +24 -0
  171. package/src/llm/comment-generator.ts +52 -103
  172. package/src/llm/index.ts +80 -80
  173. package/src/llm/prompts.ts +95 -95
  174. package/src/parser/extractor.ts +643 -256
  175. package/src/parser/index.ts +105 -205
  176. package/src/parser/langs/c.ts +860 -0
  177. package/src/parser/langs/csharp.ts +519 -0
  178. package/src/parser/langs/dart.ts +820 -0
  179. package/src/parser/langs/go.ts +437 -0
  180. package/src/parser/langs/java.ts +361 -0
  181. package/src/parser/langs/php.ts +450 -0
  182. package/src/parser/langs/python.ts +206 -0
  183. package/src/parser/langs/ruby.ts +554 -0
  184. package/src/parser/langs/rust.ts +904 -0
  185. package/src/parser/langs/swift.ts +590 -0
  186. package/src/parser/langs/typescript.ts +499 -0
  187. package/src/parser/registry.ts +88 -0
  188. package/src/parser/returns.ts +137 -0
  189. package/src/parser/spec.ts +309 -0
  190. package/src/query/arity.ts +173 -0
  191. package/src/query/callgraph.ts +473 -0
  192. package/src/query/format.ts +136 -0
  193. package/src/query/hierarchy.ts +447 -0
  194. package/src/query/resolve.ts +1972 -0
  195. package/src/query/search.ts +430 -0
  196. package/src/query/words.ts +183 -0
  197. package/src/server/index.ts +390 -79
  198. package/src/tools/context.ts +82 -0
  199. package/src/tools/explore.ts +860 -0
  200. package/src/tools/impact.ts +748 -0
  201. package/src/tools/search.ts +886 -0
  202. package/src/tools/stats.ts +633 -0
  203. package/src/types.ts +113 -45
  204. package/src/version.ts +2 -0
  205. package/src/watcher/index.ts +82 -88
  206. package/tsconfig.json +7 -4
  207. package/.mcp.json +0 -8
  208. package/commands/clast-status.md +0 -9
  209. package/dist/config/defaults.d.ts.map +0 -1
  210. package/dist/config/index.d.ts.map +0 -1
  211. package/dist/db/index.d.ts.map +0 -1
  212. package/dist/db/queries.d.ts +0 -44
  213. package/dist/db/queries.d.ts.map +0 -1
  214. package/dist/db/queries.js +0 -251
  215. package/dist/db/queries.js.map +0 -1
  216. package/dist/db/schema.d.ts +0 -3
  217. package/dist/db/schema.d.ts.map +0 -1
  218. package/dist/db/schema.js +0 -94
  219. package/dist/db/schema.js.map +0 -1
  220. package/dist/db/types.d.ts +0 -44
  221. package/dist/db/types.d.ts.map +0 -1
  222. package/dist/db/types.js +0 -2
  223. package/dist/db/types.js.map +0 -1
  224. package/dist/llm/comment-generator.d.ts.map +0 -1
  225. package/dist/llm/index.d.ts.map +0 -1
  226. package/dist/llm/prompts.d.ts.map +0 -1
  227. package/dist/parser/call-resolver.d.ts +0 -15
  228. package/dist/parser/call-resolver.d.ts.map +0 -1
  229. package/dist/parser/call-resolver.js +0 -16
  230. package/dist/parser/call-resolver.js.map +0 -1
  231. package/dist/parser/extractor.d.ts.map +0 -1
  232. package/dist/parser/index.d.ts.map +0 -1
  233. package/dist/parser/languages.d.ts +0 -11
  234. package/dist/parser/languages.d.ts.map +0 -1
  235. package/dist/parser/languages.js +0 -74
  236. package/dist/parser/languages.js.map +0 -1
  237. package/dist/server/index.d.ts.map +0 -1
  238. package/dist/server/register-tools.d.ts +0 -7
  239. package/dist/server/register-tools.d.ts.map +0 -1
  240. package/dist/server/register-tools.js +0 -17
  241. package/dist/server/register-tools.js.map +0 -1
  242. package/dist/server/tools/clast-call-graph.d.ts +0 -4
  243. package/dist/server/tools/clast-call-graph.d.ts.map +0 -1
  244. package/dist/server/tools/clast-call-graph.js +0 -70
  245. package/dist/server/tools/clast-call-graph.js.map +0 -1
  246. package/dist/server/tools/clast-comment.d.ts +0 -5
  247. package/dist/server/tools/clast-comment.d.ts.map +0 -1
  248. package/dist/server/tools/clast-comment.js +0 -64
  249. package/dist/server/tools/clast-comment.js.map +0 -1
  250. package/dist/server/tools/clast-file-summary.d.ts +0 -4
  251. package/dist/server/tools/clast-file-summary.d.ts.map +0 -1
  252. package/dist/server/tools/clast-file-summary.js +0 -42
  253. package/dist/server/tools/clast-file-summary.js.map +0 -1
  254. package/dist/server/tools/clast-get-context.d.ts +0 -4
  255. package/dist/server/tools/clast-get-context.d.ts.map +0 -1
  256. package/dist/server/tools/clast-get-context.js +0 -80
  257. package/dist/server/tools/clast-get-context.js.map +0 -1
  258. package/dist/server/tools/clast-reindex.d.ts +0 -5
  259. package/dist/server/tools/clast-reindex.d.ts.map +0 -1
  260. package/dist/server/tools/clast-reindex.js +0 -36
  261. package/dist/server/tools/clast-reindex.js.map +0 -1
  262. package/dist/server/tools/clast-search.d.ts +0 -4
  263. package/dist/server/tools/clast-search.d.ts.map +0 -1
  264. package/dist/server/tools/clast-search.js +0 -29
  265. package/dist/server/tools/clast-search.js.map +0 -1
  266. package/dist/server/tools/clast-status.d.ts +0 -5
  267. package/dist/server/tools/clast-status.d.ts.map +0 -1
  268. package/dist/server/tools/clast-status.js +0 -23
  269. package/dist/server/tools/clast-status.js.map +0 -1
  270. package/dist/types.d.ts.map +0 -1
  271. package/dist/watcher/gitignore.d.ts +0 -9
  272. package/dist/watcher/gitignore.d.ts.map +0 -1
  273. package/dist/watcher/gitignore.js +0 -38
  274. package/dist/watcher/gitignore.js.map +0 -1
  275. package/dist/watcher/index.d.ts.map +0 -1
  276. package/skills/clast-explore/SKILL.md +0 -41
  277. package/src/db/queries.ts +0 -332
  278. package/src/db/schema.ts +0 -106
  279. package/src/db/types.ts +0 -47
  280. package/src/parser/call-resolver.ts +0 -17
  281. package/src/parser/languages.ts +0 -86
  282. package/src/server/register-tools.ts +0 -30
  283. package/src/server/tools/clast-call-graph.ts +0 -82
  284. package/src/server/tools/clast-comment.ts +0 -76
  285. package/src/server/tools/clast-file-summary.ts +0 -59
  286. package/src/server/tools/clast-get-context.ts +0 -97
  287. package/src/server/tools/clast-reindex.ts +0 -52
  288. package/src/server/tools/clast-search.ts +0 -38
  289. package/src/server/tools/clast-status.ts +0 -37
  290. package/src/watcher/gitignore.ts +0 -42
@@ -0,0 +1,361 @@
1
+ import type { Node } from 'web-tree-sitter';
2
+ import type { Ctx, LangSpec, Visitor } from '../spec.js';
3
+ import { field, isConstName, lastSegment, simpleReceiver } from '../spec.js';
4
+
5
+ const MAX_SIG_SLICE = 400;
6
+ const TYPE_DECLS = [
7
+ 'class_declaration', 'interface_declaration', 'enum_declaration',
8
+ 'record_declaration', 'annotation_type_declaration',
9
+ ];
10
+
11
+ function modifiersOf(node: Node): Node | null {
12
+ const first = node.firstChild;
13
+ if (first && first.type === 'modifiers') return first;
14
+ // A leading comment inside the declaration is possible but rare.
15
+ for (const c of node.children) {
16
+ if (!c) continue;
17
+ if (c.type === 'modifiers') return c;
18
+ if (c.type !== 'line_comment' && c.type !== 'block_comment') break;
19
+ }
20
+ return null;
21
+ }
22
+
23
+ function hasModifier(mods: Node | null, keyword: string): boolean {
24
+ if (!mods) return false;
25
+ for (const c of mods.children) if (c && c.type === keyword) return true;
26
+ return false;
27
+ }
28
+
29
+ /**
30
+ * Offset where the signature starts: after leading annotations, so that
31
+ * `@Override @SuppressWarnings("x") public void foo()` yields `public void foo()`.
32
+ */
33
+ function sigStart(node: Node, mods: Node | null): number {
34
+ if (!mods) return node.startIndex;
35
+ for (const c of mods.children) {
36
+ if (!c) continue;
37
+ if (c.type === 'annotation' || c.type === 'marker_annotation') continue;
38
+ if (c.type === 'line_comment' || c.type === 'block_comment') continue;
39
+ return c.startIndex;
40
+ }
41
+ const next = mods.nextSibling;
42
+ return next ? next.startIndex : node.startIndex;
43
+ }
44
+
45
+ function sliceSig(ctx: Ctx, start: number, end: number): string {
46
+ if (end - start > MAX_SIG_SLICE) end = start + MAX_SIG_SLICE;
47
+ return ctx.src.slice(start, end).replace(/\s*[{;]\s*$/, '');
48
+ }
49
+
50
+ /** Signature from after the annotations up to the body (or the node end). */
51
+ function signature(node: Node, mods: Node | null, ctx: Ctx, body: Node | null = field(node, 'body')): string {
52
+ const start = sigStart(node, mods);
53
+ return sliceSig(ctx, start, body && body.startIndex > start ? body.startIndex : node.endIndex);
54
+ }
55
+
56
+ /** Simple name of a type node: strips generics, package qualifiers, annotations and array dims. */
57
+ function typeName(node: Node | null | undefined): string | null {
58
+ let n = node ?? null;
59
+ for (let i = 0; n && i < 4; i++) {
60
+ switch (n.type) {
61
+ case 'type_identifier':
62
+ case 'identifier':
63
+ return n.text;
64
+ case 'generic_type':
65
+ n = n.namedChild(0);
66
+ break;
67
+ case 'scoped_type_identifier':
68
+ n = n.namedChild(n.namedChildCount - 1);
69
+ break;
70
+ case 'array_type':
71
+ n = field(n, 'element');
72
+ break;
73
+ default:
74
+ if (n.endIndex - n.startIndex > 200) return null;
75
+ return lastSegment(n.text.replace(/<.*$/s, '').replace(/[\[\]\s]+$/, ''));
76
+ }
77
+ }
78
+ return null;
79
+ }
80
+
81
+ const MAX_TYPE_TEXT = 120;
82
+
83
+ /**
84
+ * Raw text of a declared type for ctx.typeHint (which normalises it), or null
85
+ * for types that never name the class of the variable: arrays, primitives.
86
+ */
87
+ function hintType(type: Node | null | undefined): string | null {
88
+ let n = type ?? null;
89
+ if (n && n.type === 'annotated_type') n = n.namedChild(n.namedChildCount - 1);
90
+ if (!n) return null;
91
+ switch (n.type) {
92
+ case 'type_identifier':
93
+ case 'scoped_type_identifier':
94
+ return n.endIndex - n.startIndex > MAX_TYPE_TEXT ? null : n.text;
95
+ case 'generic_type':
96
+ // Long generic types: the outer name is enough (wrappers are not unwrapped then).
97
+ return n.endIndex - n.startIndex > MAX_TYPE_TEXT ? typeName(n) : n.text;
98
+ default:
99
+ return null; // array_type, integral_type, boolean_type, void_type, ...
100
+ }
101
+ }
102
+
103
+ /** Declared type of a declarator: the written type, or the constructed class for `var x = new Foo()`. */
104
+ function declaredType(type: Node | null, value: Node | null): string | null {
105
+ if (!type) return null;
106
+ if (type.type === 'type_identifier' && type.endIndex - type.startIndex === 3 && type.text === 'var') {
107
+ return value && value.type === 'object_creation_expression' ? hintType(field(value, 'type')) : null;
108
+ }
109
+ return hintType(type);
110
+ }
111
+
112
+ /** Parameters, lambda parameters with explicit types and record components. */
113
+ const formalParameter: Visitor = (node, ctx) => {
114
+ if (field(node, 'dimensions')) return;
115
+ ctx.typeHint(field(node, 'name')?.text, hintType(field(node, 'type')));
116
+ };
117
+
118
+ /** Varargs `Foo... rest`: the type is the first non-modifier child, the name sits in a variable_declarator. */
119
+ const spreadParameter: Visitor = (node, ctx) => {
120
+ let type: Node | null = null;
121
+ let name: Node | null = null;
122
+ for (const c of node.namedChildren) {
123
+ if (!c) continue;
124
+ if (c.type === 'variable_declarator') name = field(c, 'name');
125
+ else if (c.type !== 'modifiers' && !type) type = c;
126
+ }
127
+ if (name) ctx.typeHint(name.text, hintType(type));
128
+ };
129
+
130
+ /** `catch (IOException e)`; multi-catch `A | B e` has no single type. */
131
+ const catchParameter: Visitor = (node, ctx) => {
132
+ const name = field(node, 'name');
133
+ if (!name) return;
134
+ for (const c of node.namedChildren) {
135
+ if (!c || c.type !== 'catch_type') continue;
136
+ if (c.namedChildCount === 1) ctx.typeHint(name.text, hintType(c.namedChild(0)));
137
+ return;
138
+ }
139
+ };
140
+
141
+ /** `for (Item it : items)` and try-with-resources `Reader r = open()`. */
142
+ const typedName: Visitor = (node, ctx) => {
143
+ const name = field(node, 'name');
144
+ if (name && name.type === 'identifier') ctx.typeHint(name.text, declaredType(field(node, 'type'), field(node, 'value')));
145
+ };
146
+
147
+ /** Pattern variable: `o instanceof Foo f`. */
148
+ const instanceofExpression: Visitor = (node, ctx) => {
149
+ const name = field(node, 'name');
150
+ if (name) ctx.typeHint(name.text, hintType(field(node, 'right')));
151
+ };
152
+
153
+ /** True when a member sits in an anonymous class body (`new Foo() { ... }`). */
154
+ function inAnonymousClass(member: Node): boolean {
155
+ const body = member.parent;
156
+ return !!body && body.type === 'class_body' && body.parent?.type === 'object_creation_expression';
157
+ }
158
+
159
+ function memberExported(mods: Node | null, ctx: Ctx): boolean {
160
+ if (hasModifier(mods, 'public')) return true;
161
+ return ctx.parentKind() === 'interface' && !hasModifier(mods, 'private');
162
+ }
163
+
164
+ function typeList(sym: number, list: Node | null, kind: 'extends' | 'implements', ctx: Ctx): void {
165
+ if (!list) return;
166
+ for (const c of list.namedChildren) {
167
+ if (!c) continue;
168
+ if (c.type === 'type_list') typeList(sym, c, kind, ctx);
169
+ else ctx.inherit(sym, typeName(c), kind);
170
+ }
171
+ }
172
+
173
+ const typeDecl = (kind: 'class' | 'interface' | 'enum'): Visitor => (node, ctx) => {
174
+ const name = field(node, 'name');
175
+ if (!name) return;
176
+ const mods = modifiersOf(node);
177
+ const exported = hasModifier(mods, 'public');
178
+ const sym = ctx.def(node, { name: name.text, kind, sig: signature(node, mods, ctx), exported });
179
+
180
+ for (const c of node.namedChildren) {
181
+ if (!c) continue;
182
+ if (c.type === 'superclass') typeList(sym, c, 'extends', ctx);
183
+ else if (c.type === 'super_interfaces') typeList(sym, c, 'implements', ctx);
184
+ else if (c.type === 'extends_interfaces') typeList(sym, c, 'extends', ctx);
185
+ }
186
+
187
+ if (node.type === 'record_declaration') {
188
+ // Record components are implicit private fields with public accessors.
189
+ for (const p of field(node, 'parameters')?.namedChildren ?? []) {
190
+ if (!p || p.type !== 'formal_parameter') continue;
191
+ const pName = field(p, 'name');
192
+ if (pName) ctx.def(p, { name: pName.text, kind: 'field', sig: signature(p, modifiersOf(p), ctx, null), scope: false, exported });
193
+ }
194
+ }
195
+ };
196
+
197
+ const method: Visitor = (node, ctx) => {
198
+ const name = field(node, 'name');
199
+ if (!name) return;
200
+ // Methods of anonymous classes are not symbols; calls inside them are
201
+ // attributed to the enclosing method / field.
202
+ if (inAnonymousClass(node)) return;
203
+ const mods = modifiersOf(node);
204
+ ctx.def(node, {
205
+ name: name.text,
206
+ kind: 'method',
207
+ sig: signature(node, mods, ctx),
208
+ exported: memberExported(mods, ctx),
209
+ });
210
+ };
211
+
212
+ const annotationElement: Visitor = (node, ctx) => {
213
+ const name = field(node, 'name');
214
+ if (!name) return;
215
+ ctx.def(node, { name: name.text, kind: 'method', sig: signature(node, modifiersOf(node), ctx, null), exported: true });
216
+ };
217
+
218
+ const enumConstant: Visitor = (node, ctx) => {
219
+ const name = field(node, 'name');
220
+ if (!name) return;
221
+ const enumDecl = node.parent?.parent;
222
+ const mods = modifiersOf(node);
223
+ ctx.def(node, {
224
+ name: name.text,
225
+ kind: 'constant',
226
+ sig: signature(node, mods, ctx),
227
+ exported: !!enumDecl && hasModifier(modifiersOf(enumDecl), 'public'),
228
+ });
229
+ };
230
+
231
+ /**
232
+ * Fields and interface constants: one symbol per declarator. Locals are not
233
+ * symbols; they only contribute a receiver type hint.
234
+ */
235
+ const variableDeclarator: Visitor = (node, ctx) => {
236
+ const decl = node.parent;
237
+ if (!decl) return;
238
+ const isInterfaceConst = decl.type === 'constant_declaration';
239
+ const isField = isInterfaceConst || decl.type === 'field_declaration';
240
+ if (!isField && decl.type !== 'local_variable_declaration') return;
241
+ const nameNode = field(node, 'name');
242
+ if (!nameNode) return;
243
+ const name = nameNode.text;
244
+ const type = field(node, 'dimensions') ? null : declaredType(field(decl, 'type'), field(node, 'value'));
245
+ if (!isField || inAnonymousClass(decl)) {
246
+ if (type) ctx.typeHint(name, type);
247
+ return;
248
+ }
249
+ const mods = modifiersOf(decl);
250
+
251
+ // Signature: modifiers + type + this declarator (with a short initializer).
252
+ const start = sigStart(decl, mods);
253
+ const value = field(node, 'value');
254
+ const declEnd = value && node.endIndex - node.startIndex > 100 ? value.startIndex : node.endIndex;
255
+ let sig: string;
256
+ if (start <= node.startIndex) {
257
+ const first = decl.childForFieldName('declarator');
258
+ const head = ctx.src.slice(start, Math.min(first ? first.startIndex : node.startIndex, start + MAX_SIG_SLICE));
259
+ sig = head + ctx.src.slice(node.startIndex, declEnd);
260
+ } else {
261
+ sig = ctx.src.slice(node.startIndex, declEnd);
262
+ }
263
+ sig = sig.replace(/\s*=\s*$/, '');
264
+
265
+ const isConst = isInterfaceConst || (hasModifier(mods, 'static') && hasModifier(mods, 'final') && isConstName(name));
266
+ ctx.def(node, {
267
+ name,
268
+ kind: isConst ? 'constant' : 'field',
269
+ range: decl,
270
+ sig,
271
+ exported: memberExported(mods, ctx),
272
+ });
273
+ // The field symbol is not a container, so the hint lands on the enclosing class.
274
+ if (type) ctx.typeHint(name, type);
275
+ };
276
+
277
+ const methodInvocation: Visitor = (node, ctx) => {
278
+ const name = field(node, 'name');
279
+ if (!name) return;
280
+ const object = field(node, 'object');
281
+ if (!object) {
282
+ ctx.call(name.text, node);
283
+ return;
284
+ }
285
+ // `Outer.super.foo()`: object is `Outer`, followed by `.` and `super`.
286
+ const qualifiedSuper = object.nextSibling?.nextSibling?.type === 'super';
287
+ ctx.call(name.text, node, qualifiedSuper ? 'super' : simpleReceiver(object));
288
+ };
289
+
290
+ const objectCreation: Visitor = (node, ctx) => {
291
+ ctx.call(typeName(field(node, 'type')), node);
292
+ };
293
+
294
+ /** `this(...)` / `super(...)` inside a constructor. */
295
+ const explicitConstructorInvocation: Visitor = (node, ctx) => {
296
+ const target = field(node, 'constructor');
297
+ if (!target) return;
298
+ let cls: Node | null = node.parent;
299
+ for (let i = 0; cls && i < 6 && !TYPE_DECLS.includes(cls.type); i++) cls = cls.parent;
300
+ if (!cls || !TYPE_DECLS.includes(cls.type)) return;
301
+ if (target.type === 'this') {
302
+ ctx.call(field(cls, 'name')?.text, node);
303
+ } else if (target.type === 'super') {
304
+ const sup = field(cls, 'superclass');
305
+ if (sup) ctx.call(typeName(sup.namedChild(0)), node);
306
+ }
307
+ };
308
+
309
+ /** `Foo::bar` counts as a call to `bar`; `Foo::new` as a call to `Foo`. */
310
+ const methodReference: Visitor = (node, ctx) => {
311
+ const target = node.child(0);
312
+ const member = node.child(node.childCount - 1);
313
+ if (!target || !member) return;
314
+ if (member.type === 'identifier') ctx.call(member.text, node, simpleReceiver(target));
315
+ else if (member.type === 'new') ctx.call(typeName(target), node);
316
+ };
317
+
318
+ const importDeclaration: Visitor = (node, ctx) => {
319
+ let name: Node | null = null;
320
+ let wildcard = false;
321
+ for (const c of node.namedChildren) {
322
+ if (!c) continue;
323
+ if (c.type === 'scoped_identifier' || c.type === 'identifier') name = c;
324
+ else if (c.type === 'asterisk') wildcard = true;
325
+ }
326
+ if (!name) return;
327
+ const source = name.text.replace(/\s+/g, '');
328
+ if (wildcard) ctx.imp(source + '.*', node, ['*']);
329
+ else ctx.imp(source, node, [lastSegment(source)]);
330
+ };
331
+
332
+ export const java: LangSpec = {
333
+ id: 'java',
334
+ grammar: 'java',
335
+ extensions: ['.java'],
336
+ identifiers: ['identifier', 'type_identifier'],
337
+ visit: {
338
+ class_declaration: typeDecl('class'),
339
+ record_declaration: typeDecl('class'),
340
+ interface_declaration: typeDecl('interface'),
341
+ annotation_type_declaration: typeDecl('interface'),
342
+ enum_declaration: typeDecl('enum'),
343
+ enum_constant: enumConstant,
344
+ method_declaration: method,
345
+ constructor_declaration: method,
346
+ compact_constructor_declaration: method,
347
+ annotation_type_element_declaration: annotationElement,
348
+ variable_declarator: variableDeclarator,
349
+ formal_parameter: formalParameter,
350
+ spread_parameter: spreadParameter,
351
+ catch_formal_parameter: catchParameter,
352
+ enhanced_for_statement: typedName,
353
+ resource: typedName,
354
+ instanceof_expression: instanceofExpression,
355
+ method_invocation: methodInvocation,
356
+ object_creation_expression: objectCreation,
357
+ explicit_constructor_invocation: explicitConstructorInvocation,
358
+ method_reference: methodReference,
359
+ import_declaration: importDeclaration,
360
+ },
361
+ };