ucn 3.8.10 → 3.8.12

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/languages/java.js CHANGED
@@ -7,6 +7,7 @@
7
7
 
8
8
  const {
9
9
  traverseTree,
10
+ traverseTreeCached,
10
11
  nodeToLocation,
11
12
  parseStructuredParams,
12
13
  extractJavaDocstring
@@ -120,96 +121,106 @@ function extractGenerics(node) {
120
121
  }
121
122
 
122
123
  /**
123
- * Find all methods/constructors in Java code using tree-sitter
124
+ * Process a node for function/method extraction (single-pass helper)
125
+ * Returns true if node was matched, false otherwise
124
126
  */
125
- function findFunctions(code, parser) {
126
- const tree = parseTree(parser, code);
127
- const functions = [];
128
- const processedRanges = new Set();
129
-
130
- traverseTree(tree.rootNode, (node) => {
127
+ function _processFunction(node, functions, processedRanges, lines, code) {
128
+ // Method declarations
129
+ if (node.type === 'method_declaration') {
131
130
  const rangeKey = `${node.startIndex}-${node.endIndex}`;
131
+ if (processedRanges.has(rangeKey)) return true;
132
+ processedRanges.add(rangeKey);
132
133
 
133
- // Method declarations
134
- if (node.type === 'method_declaration') {
135
- if (processedRanges.has(rangeKey)) return true;
136
- processedRanges.add(rangeKey);
134
+ // Skip methods inside a class/interface/enum body (they're extracted as class members)
135
+ let parent = node.parent;
136
+ if (parent && (parent.type === 'class_body' || parent.type === 'interface_body' || parent.type === 'enum_body' || parent.type === 'enum_body_declarations')) {
137
+ return true; // Skip - this is a class/interface/enum method
138
+ }
137
139
 
138
- // Skip methods inside a class/interface/enum body (they're extracted as class members)
139
- let parent = node.parent;
140
- if (parent && (parent.type === 'class_body' || parent.type === 'interface_body' || parent.type === 'enum_body' || parent.type === 'enum_body_declarations')) {
141
- return true; // Skip - this is a class/interface/enum method
142
- }
140
+ const nameNode = node.childForFieldName('name');
141
+ const paramsNode = node.childForFieldName('parameters');
143
142
 
144
- const nameNode = node.childForFieldName('name');
145
- const paramsNode = node.childForFieldName('parameters');
143
+ if (nameNode) {
144
+ const { startLine, endLine, indent } = nodeToLocation(node, lines);
145
+ const modifiers = extractModifiers(node);
146
+ const annotations = extractAnnotations(node);
147
+ const returnType = extractReturnType(node);
148
+ const generics = extractGenerics(node);
149
+ const docstring = extractJavaDocstring(lines, startLine);
150
+ // nameLine: where the name identifier lives (differs from startLine when annotations are present)
151
+ const nameLine = nameNode.startPosition.row + 1;
152
+
153
+ functions.push({
154
+ name: nameNode.text,
155
+ params: extractJavaParams(paramsNode),
156
+ paramsStructured: parseStructuredParams(paramsNode, 'java'),
157
+ startLine,
158
+ endLine,
159
+ indent,
160
+ modifiers,
161
+ ...(returnType && { returnType }),
162
+ ...(generics && { generics }),
163
+ ...(docstring && { docstring }),
164
+ ...(annotations.length > 0 && { annotations }),
165
+ ...(nameLine !== startLine && { nameLine })
166
+ });
167
+ }
168
+ return true;
169
+ }
146
170
 
147
- if (nameNode) {
148
- const { startLine, endLine, indent } = nodeToLocation(node, code);
149
- const modifiers = extractModifiers(node);
150
- const annotations = extractAnnotations(node);
151
- const returnType = extractReturnType(node);
152
- const generics = extractGenerics(node);
153
- const docstring = extractJavaDocstring(code, startLine);
154
- // nameLine: where the name identifier lives (differs from startLine when annotations are present)
155
- const nameLine = nameNode.startPosition.row + 1;
171
+ // Constructor declarations
172
+ if (node.type === 'constructor_declaration') {
173
+ const rangeKey = `${node.startIndex}-${node.endIndex}`;
174
+ if (processedRanges.has(rangeKey)) return true;
175
+ processedRanges.add(rangeKey);
156
176
 
157
- functions.push({
158
- name: nameNode.text,
159
- params: extractJavaParams(paramsNode),
160
- paramsStructured: parseStructuredParams(paramsNode, 'java'),
161
- startLine,
162
- endLine,
163
- indent,
164
- modifiers,
165
- ...(returnType && { returnType }),
166
- ...(generics && { generics }),
167
- ...(docstring && { docstring }),
168
- ...(annotations.length > 0 && { annotations }),
169
- ...(nameLine !== startLine && { nameLine })
170
- });
171
- }
172
- return true;
177
+ // Skip constructors inside a class/enum body (they're extracted as class members)
178
+ let parent = node.parent;
179
+ if (parent && (parent.type === 'class_body' || parent.type === 'enum_body' || parent.type === 'enum_body_declarations')) {
180
+ return true; // Skip - this is a class/enum constructor
173
181
  }
174
182
 
175
- // Constructor declarations
176
- if (node.type === 'constructor_declaration') {
177
- if (processedRanges.has(rangeKey)) return true;
178
- processedRanges.add(rangeKey);
179
-
180
- // Skip constructors inside a class/enum body (they're extracted as class members)
181
- let parent = node.parent;
182
- if (parent && (parent.type === 'class_body' || parent.type === 'enum_body' || parent.type === 'enum_body_declarations')) {
183
- return true; // Skip - this is a class/enum constructor
184
- }
183
+ const nameNode = node.childForFieldName('name');
184
+ const paramsNode = node.childForFieldName('parameters');
185
185
 
186
- const nameNode = node.childForFieldName('name');
187
- const paramsNode = node.childForFieldName('parameters');
186
+ if (nameNode) {
187
+ const { startLine, endLine, indent } = nodeToLocation(node, lines);
188
+ const modifiers = extractModifiers(node);
189
+ const annotations = extractAnnotations(node);
190
+ const docstring = extractJavaDocstring(lines, startLine);
191
+ const nameLine = nameNode.startPosition.row + 1;
192
+
193
+ functions.push({
194
+ name: nameNode.text,
195
+ params: extractJavaParams(paramsNode),
196
+ paramsStructured: parseStructuredParams(paramsNode, 'java'),
197
+ startLine,
198
+ endLine,
199
+ indent,
200
+ modifiers,
201
+ isConstructor: true,
202
+ ...(docstring && { docstring }),
203
+ ...(annotations.length > 0 && { annotations }),
204
+ ...(nameLine !== startLine && { nameLine })
205
+ });
206
+ }
207
+ return true;
208
+ }
188
209
 
189
- if (nameNode) {
190
- const { startLine, endLine, indent } = nodeToLocation(node, code);
191
- const modifiers = extractModifiers(node);
192
- const annotations = extractAnnotations(node);
193
- const docstring = extractJavaDocstring(code, startLine);
194
- const nameLine = nameNode.startPosition.row + 1;
210
+ return false;
211
+ }
195
212
 
196
- functions.push({
197
- name: nameNode.text,
198
- params: extractJavaParams(paramsNode),
199
- paramsStructured: parseStructuredParams(paramsNode, 'java'),
200
- startLine,
201
- endLine,
202
- indent,
203
- modifiers,
204
- isConstructor: true,
205
- ...(docstring && { docstring }),
206
- ...(annotations.length > 0 && { annotations }),
207
- ...(nameLine !== startLine && { nameLine })
208
- });
209
- }
210
- return true;
211
- }
213
+ /**
214
+ * Find all methods/constructors in Java code using tree-sitter
215
+ */
216
+ function findFunctions(code, parser) {
217
+ const tree = parseTree(parser, code);
218
+ const lines = code.split('\n');
219
+ const functions = [];
220
+ const processedRanges = new Set();
212
221
 
222
+ traverseTreeCached(tree.rootNode, (node) => {
223
+ _processFunction(node, functions, processedRanges, lines, code);
213
224
  return true;
214
225
  });
215
226
 
@@ -218,165 +229,176 @@ function findFunctions(code, parser) {
218
229
  }
219
230
 
220
231
  /**
221
- * Find all classes, interfaces, enums, records in Java code
232
+ * Process a node for class/interface/enum/record extraction (single-pass helper)
233
+ * Returns true if node was matched, false otherwise
222
234
  */
223
- function findClasses(code, parser) {
224
- const tree = parseTree(parser, code);
225
- const classes = [];
226
- const processedRanges = new Set();
227
-
228
- traverseTree(tree.rootNode, (node) => {
235
+ function _processClass(node, classes, processedRanges, lines, code) {
236
+ // Class declarations
237
+ if (node.type === 'class_declaration') {
229
238
  const rangeKey = `${node.startIndex}-${node.endIndex}`;
239
+ if (processedRanges.has(rangeKey)) return true;
240
+ processedRanges.add(rangeKey);
230
241
 
231
- // Class declarations
232
- if (node.type === 'class_declaration') {
233
- if (processedRanges.has(rangeKey)) return true;
234
- processedRanges.add(rangeKey);
235
-
236
- const nameNode = node.childForFieldName('name');
237
- if (nameNode) {
238
- const { startLine, endLine } = nodeToLocation(node, code);
239
- const members = extractClassMembers(node, code);
240
- const modifiers = extractModifiers(node);
241
- const annotations = extractAnnotations(node);
242
- const docstring = extractJavaDocstring(code, startLine);
243
- const generics = extractGenerics(node);
244
- const extendsInfo = extractExtends(node);
245
- const implementsInfo = extractImplements(node);
246
-
247
- // Check if this is a nested/inner class
248
- let parentNode = node.parent;
249
- const isNested = parentNode && parentNode.type === 'class_body';
250
-
251
- classes.push({
252
- name: nameNode.text,
253
- startLine,
254
- endLine,
255
- type: 'class',
256
- members,
257
- modifiers,
258
- ...(isNested && { isNested: true }),
259
- ...(docstring && { docstring }),
260
- ...(generics && { generics }),
261
- ...(annotations.length > 0 && { annotations }),
262
- ...(extendsInfo && { extends: extendsInfo }),
263
- ...(implementsInfo.length > 0 && { implements: implementsInfo })
264
- });
265
- }
266
- // Continue traversal to find inner classes, but members are already extracted
267
- return true;
242
+ const nameNode = node.childForFieldName('name');
243
+ if (nameNode) {
244
+ const { startLine, endLine } = nodeToLocation(node, lines);
245
+ const members = extractClassMembers(node, lines);
246
+ const modifiers = extractModifiers(node);
247
+ const annotations = extractAnnotations(node);
248
+ const docstring = extractJavaDocstring(lines, startLine);
249
+ const generics = extractGenerics(node);
250
+ const extendsInfo = extractExtends(node);
251
+ const implementsInfo = extractImplements(node);
252
+
253
+ // Check if this is a nested/inner class
254
+ let parentNode = node.parent;
255
+ const isNested = parentNode && parentNode.type === 'class_body';
256
+
257
+ classes.push({
258
+ name: nameNode.text,
259
+ startLine,
260
+ endLine,
261
+ type: 'class',
262
+ members,
263
+ modifiers,
264
+ ...(isNested && { isNested: true }),
265
+ ...(docstring && { docstring }),
266
+ ...(generics && { generics }),
267
+ ...(annotations.length > 0 && { annotations }),
268
+ ...(extendsInfo && { extends: extendsInfo }),
269
+ ...(implementsInfo.length > 0 && { implements: implementsInfo })
270
+ });
268
271
  }
272
+ return true;
273
+ }
269
274
 
270
- // Interface declarations
271
- if (node.type === 'interface_declaration') {
272
- if (processedRanges.has(rangeKey)) return true;
273
- processedRanges.add(rangeKey);
274
-
275
- const nameNode = node.childForFieldName('name');
276
- if (nameNode) {
277
- const { startLine, endLine } = nodeToLocation(node, code);
278
- const modifiers = extractModifiers(node);
279
- const annotations = extractAnnotations(node);
280
- const docstring = extractJavaDocstring(code, startLine);
281
- const generics = extractGenerics(node);
282
- const extendsInfo = extractInterfaceExtends(node);
275
+ // Interface declarations
276
+ if (node.type === 'interface_declaration') {
277
+ const rangeKey = `${node.startIndex}-${node.endIndex}`;
278
+ if (processedRanges.has(rangeKey)) return true;
279
+ processedRanges.add(rangeKey);
283
280
 
284
- classes.push({
285
- name: nameNode.text,
286
- startLine,
287
- endLine,
288
- type: 'interface',
289
- members: extractClassMembers(node, code),
290
- modifiers,
291
- ...(docstring && { docstring }),
292
- ...(generics && { generics }),
293
- ...(annotations.length > 0 && { annotations }),
294
- ...(extendsInfo.length > 0 && { extends: extendsInfo.join(', ') })
295
- });
296
- }
297
- return false;
281
+ const nameNode = node.childForFieldName('name');
282
+ if (nameNode) {
283
+ const { startLine, endLine } = nodeToLocation(node, lines);
284
+ const modifiers = extractModifiers(node);
285
+ const annotations = extractAnnotations(node);
286
+ const docstring = extractJavaDocstring(lines, startLine);
287
+ const generics = extractGenerics(node);
288
+ const extendsInfo = extractInterfaceExtends(node);
289
+
290
+ classes.push({
291
+ name: nameNode.text,
292
+ startLine,
293
+ endLine,
294
+ type: 'interface',
295
+ members: extractClassMembers(node, lines),
296
+ modifiers,
297
+ ...(docstring && { docstring }),
298
+ ...(generics && { generics }),
299
+ ...(annotations.length > 0 && { annotations }),
300
+ ...(extendsInfo.length > 0 && { extends: extendsInfo.join(', ') })
301
+ });
298
302
  }
303
+ return true;
304
+ }
299
305
 
300
- // Enum declarations
301
- if (node.type === 'enum_declaration') {
302
- if (processedRanges.has(rangeKey)) return true;
303
- processedRanges.add(rangeKey);
304
-
305
- const nameNode = node.childForFieldName('name');
306
- if (nameNode) {
307
- const { startLine, endLine } = nodeToLocation(node, code);
308
- const modifiers = extractModifiers(node);
309
- const annotations = extractAnnotations(node);
310
- const docstring = extractJavaDocstring(code, startLine);
306
+ // Enum declarations
307
+ if (node.type === 'enum_declaration') {
308
+ const rangeKey = `${node.startIndex}-${node.endIndex}`;
309
+ if (processedRanges.has(rangeKey)) return true;
310
+ processedRanges.add(rangeKey);
311
311
 
312
- classes.push({
313
- name: nameNode.text,
314
- startLine,
315
- endLine,
316
- type: 'enum',
317
- members: extractEnumConstants(node, code),
318
- modifiers,
319
- ...(docstring && { docstring }),
320
- ...(annotations.length > 0 && { annotations })
321
- });
322
- }
323
- return false;
312
+ const nameNode = node.childForFieldName('name');
313
+ if (nameNode) {
314
+ const { startLine, endLine } = nodeToLocation(node, lines);
315
+ const modifiers = extractModifiers(node);
316
+ const annotations = extractAnnotations(node);
317
+ const docstring = extractJavaDocstring(lines, startLine);
318
+
319
+ classes.push({
320
+ name: nameNode.text,
321
+ startLine,
322
+ endLine,
323
+ type: 'enum',
324
+ members: extractEnumConstants(node, lines),
325
+ modifiers,
326
+ ...(docstring && { docstring }),
327
+ ...(annotations.length > 0 && { annotations })
328
+ });
324
329
  }
330
+ return true;
331
+ }
325
332
 
326
- // Record declarations (Java 14+)
327
- if (node.type === 'record_declaration') {
328
- if (processedRanges.has(rangeKey)) return true;
329
- processedRanges.add(rangeKey);
333
+ // Record declarations (Java 14+)
334
+ if (node.type === 'record_declaration') {
335
+ const rangeKey = `${node.startIndex}-${node.endIndex}`;
336
+ if (processedRanges.has(rangeKey)) return true;
337
+ processedRanges.add(rangeKey);
330
338
 
331
- const nameNode = node.childForFieldName('name');
332
- if (nameNode) {
333
- const { startLine, endLine } = nodeToLocation(node, code);
334
- const modifiers = extractModifiers(node);
335
- const annotations = extractAnnotations(node);
336
- const docstring = extractJavaDocstring(code, startLine);
337
- const generics = extractGenerics(node);
338
- const implementsInfo = extractImplements(node);
339
-
340
- // Extract record components as members
341
- const members = extractClassMembers(node, code);
342
- // Also extract record components from formal_parameters
343
- const paramsNode = node.childForFieldName('parameters');
344
- if (paramsNode) {
345
- for (let pi = 0; pi < paramsNode.namedChildCount; pi++) {
346
- const param = paramsNode.namedChild(pi);
347
- if (param.type === 'formal_parameter' || param.type === 'spread_parameter') {
348
- const pName = param.childForFieldName('name');
349
- const pType = param.childForFieldName('type');
350
- if (pName) {
351
- const { startLine: pLine, endLine: pEnd } = nodeToLocation(param, code);
352
- members.push({
353
- name: pName.text,
354
- startLine: pLine,
355
- endLine: pEnd,
356
- memberType: 'field',
357
- ...(pType && { fieldType: pType.text })
358
- });
359
- }
339
+ const nameNode = node.childForFieldName('name');
340
+ if (nameNode) {
341
+ const { startLine, endLine } = nodeToLocation(node, lines);
342
+ const modifiers = extractModifiers(node);
343
+ const annotations = extractAnnotations(node);
344
+ const docstring = extractJavaDocstring(lines, startLine);
345
+ const generics = extractGenerics(node);
346
+ const implementsInfo = extractImplements(node);
347
+
348
+ // Extract record components as members
349
+ const members = extractClassMembers(node, lines);
350
+ // Also extract record components from formal_parameters
351
+ const paramsNode = node.childForFieldName('parameters');
352
+ if (paramsNode) {
353
+ for (let pi = 0; pi < paramsNode.namedChildCount; pi++) {
354
+ const param = paramsNode.namedChild(pi);
355
+ if (param.type === 'formal_parameter' || param.type === 'spread_parameter') {
356
+ const pName = param.childForFieldName('name');
357
+ const pType = param.childForFieldName('type');
358
+ if (pName) {
359
+ const { startLine: pLine, endLine: pEnd } = nodeToLocation(param, lines);
360
+ members.push({
361
+ name: pName.text,
362
+ startLine: pLine,
363
+ endLine: pEnd,
364
+ memberType: 'field',
365
+ ...(pType && { fieldType: pType.text })
366
+ });
360
367
  }
361
368
  }
362
369
  }
363
-
364
- classes.push({
365
- name: nameNode.text,
366
- startLine,
367
- endLine,
368
- type: 'record',
369
- members,
370
- modifiers,
371
- ...(docstring && { docstring }),
372
- ...(generics && { generics }),
373
- ...(annotations.length > 0 && { annotations }),
374
- ...(implementsInfo.length > 0 && { implements: implementsInfo })
375
- });
376
370
  }
377
- return false;
371
+
372
+ classes.push({
373
+ name: nameNode.text,
374
+ startLine,
375
+ endLine,
376
+ type: 'record',
377
+ members,
378
+ modifiers,
379
+ ...(docstring && { docstring }),
380
+ ...(generics && { generics }),
381
+ ...(annotations.length > 0 && { annotations }),
382
+ ...(implementsInfo.length > 0 && { implements: implementsInfo })
383
+ });
378
384
  }
385
+ return true;
386
+ }
387
+
388
+ return false;
389
+ }
390
+
391
+ /**
392
+ * Find all classes, interfaces, enums, records in Java code
393
+ */
394
+ function findClasses(code, parser) {
395
+ const tree = parseTree(parser, code);
396
+ const lines = code.split('\n');
397
+ const classes = [];
398
+ const processedRanges = new Set();
379
399
 
400
+ traverseTreeCached(tree.rootNode, (node) => {
401
+ _processClass(node, classes, processedRanges, lines, code);
380
402
  return true;
381
403
  });
382
404
 
@@ -449,7 +471,8 @@ function extractInterfaceExtends(interfaceNode) {
449
471
  /**
450
472
  * Extract enum constants from enum body
451
473
  */
452
- function extractEnumConstants(enumNode, code) {
474
+ function extractEnumConstants(enumNode, codeOrLines) {
475
+ const code = codeOrLines;
453
476
  const constants = [];
454
477
  const bodyNode = enumNode.childForFieldName('body');
455
478
  if (!bodyNode) return constants;
@@ -525,7 +548,8 @@ function extractEnumConstants(enumNode, code) {
525
548
  /**
526
549
  * Extract class members (methods, constructors)
527
550
  */
528
- function extractClassMembers(classNode, code) {
551
+ function extractClassMembers(classNode, codeOrLines) {
552
+ const code = codeOrLines;
529
553
  const members = [];
530
554
  const bodyNode = classNode.childForFieldName('body');
531
555
  if (!bodyNode) return members;
@@ -606,38 +630,48 @@ function extractClassMembers(classNode, code) {
606
630
  return members;
607
631
  }
608
632
 
633
+ const _statePattern = /^([A-Z][A-Z0-9_]+|[A-Z][a-zA-Z]*(?:CONFIG|SETTINGS|OPTIONS))$/;
634
+
609
635
  /**
610
- * Find state objects (static final constants) in Java code
636
+ * Process a node for state object extraction (single-pass helper)
637
+ * Returns true if node was matched, false otherwise
611
638
  */
612
- function findStateObjects(code, parser) {
613
- const tree = parseTree(parser, code);
614
- const objects = [];
615
-
616
- const statePattern = /^([A-Z][A-Z0-9_]+|[A-Z][a-zA-Z]*(?:CONFIG|SETTINGS|OPTIONS))$/;
639
+ function _processState(node, objects, lines, code) {
640
+ if (node.type === 'field_declaration') {
641
+ const modifiers = extractModifiers(node);
642
+ if (modifiers.includes('static') && modifiers.includes('final')) {
643
+ for (let i = 0; i < node.namedChildCount; i++) {
644
+ const child = node.namedChild(i);
645
+ if (child.type === 'variable_declarator') {
646
+ const nameNode = child.childForFieldName('name');
647
+ const valueNode = child.childForFieldName('value');
617
648
 
618
- traverseTree(tree.rootNode, (node) => {
619
- if (node.type === 'field_declaration') {
620
- const modifiers = extractModifiers(node);
621
- if (modifiers.includes('static') && modifiers.includes('final')) {
622
- for (let i = 0; i < node.namedChildCount; i++) {
623
- const child = node.namedChild(i);
624
- if (child.type === 'variable_declarator') {
625
- const nameNode = child.childForFieldName('name');
626
- const valueNode = child.childForFieldName('value');
627
-
628
- if (nameNode && valueNode) {
629
- const name = nameNode.text;
630
- if (statePattern.test(name)) {
631
- const { startLine, endLine } = nodeToLocation(node, code);
632
- objects.push({ name, startLine, endLine, modifiers });
633
- }
649
+ if (nameNode && valueNode) {
650
+ const name = nameNode.text;
651
+ if (_statePattern.test(name)) {
652
+ const { startLine, endLine } = nodeToLocation(node, lines);
653
+ objects.push({ name, startLine, endLine, modifiers });
634
654
  }
635
655
  }
636
656
  }
637
657
  }
638
- return true;
639
658
  }
659
+ return true;
660
+ }
661
+
662
+ return false;
663
+ }
664
+
665
+ /**
666
+ * Find state objects (static final constants) in Java code
667
+ */
668
+ function findStateObjects(code, parser) {
669
+ const tree = parseTree(parser, code);
670
+ const lines = code.split('\n');
671
+ const objects = [];
640
672
 
673
+ traverseTreeCached(tree.rootNode, (node) => {
674
+ _processState(node, objects, lines, code);
641
675
  return true;
642
676
  });
643
677
 
@@ -649,12 +683,31 @@ function findStateObjects(code, parser) {
649
683
  * Parse a Java file completely
650
684
  */
651
685
  function parse(code, parser) {
686
+ const tree = parseTree(parser, code);
687
+ const lines = code.split('\n');
688
+ const functions = [];
689
+ const classes = [];
690
+ const stateObjects = [];
691
+ const processedFn = new Set();
692
+ const processedCls = new Set();
693
+
694
+ traverseTreeCached(tree.rootNode, (node) => {
695
+ _processFunction(node, functions, processedFn, lines, code);
696
+ _processClass(node, classes, processedCls, lines, code);
697
+ _processState(node, stateObjects, lines, code);
698
+ return true;
699
+ });
700
+
701
+ functions.sort((a, b) => a.startLine - b.startLine);
702
+ classes.sort((a, b) => a.startLine - b.startLine);
703
+ stateObjects.sort((a, b) => a.startLine - b.startLine);
704
+
652
705
  return {
653
706
  language: 'java',
654
- totalLines: code.split('\n').length,
655
- functions: findFunctions(code, parser),
656
- classes: findClasses(code, parser),
657
- stateObjects: findStateObjects(code, parser),
707
+ totalLines: lines.length,
708
+ functions,
709
+ classes,
710
+ stateObjects,
658
711
  imports: [],
659
712
  exports: []
660
713
  };
@@ -882,7 +935,7 @@ function findImportsInCode(code, parser) {
882
935
  const tree = parseTree(parser, code);
883
936
  const imports = [];
884
937
 
885
- traverseTree(tree.rootNode, (node) => {
938
+ traverseTreeCached(tree.rootNode, (node) => {
886
939
  if (node.type === 'import_declaration') {
887
940
  const line = node.startPosition.row + 1;
888
941
  let modulePath = null;
@@ -938,7 +991,7 @@ function findExportsInCode(code, parser) {
938
991
  return false;
939
992
  }
940
993
 
941
- traverseTree(tree.rootNode, (node) => {
994
+ traverseTreeCached(tree.rootNode, (node) => {
942
995
  // Public classes
943
996
  if (node.type === 'class_declaration' && isPublic(node)) {
944
997
  const nameNode = node.childForFieldName('name');
@@ -1008,7 +1061,7 @@ function findUsagesInCode(code, name, parser) {
1008
1061
  const tree = parseTree(parser, code);
1009
1062
  const usages = [];
1010
1063
 
1011
- traverseTree(tree.rootNode, (node) => {
1064
+ traverseTreeCached(tree.rootNode, (node) => {
1012
1065
  // Look for identifiers and type_identifiers with the matching name
1013
1066
  // type_identifier is used in Java for type references: new ClassName(), extends ClassName, field types
1014
1067
  if ((node.type !== 'identifier' && node.type !== 'type_identifier') || node.text !== name) {