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/.github/workflows/ci.yml +33 -0
- package/.github/workflows/publish.yml +67 -0
- package/README.md +5 -2
- package/core/project.js +30 -9
- package/languages/go.js +249 -216
- package/languages/java.js +303 -250
- package/languages/javascript.js +463 -412
- package/languages/python.js +189 -148
- package/languages/rust.js +394 -337
- package/languages/utils.js +89 -10
- package/mcp/server.js +65 -49
- package/package.json +1 -1
- package/.claude/scheduled_tasks.lock +0 -1
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
|
-
*
|
|
124
|
+
* Process a node for function/method extraction (single-pass helper)
|
|
125
|
+
* Returns true if node was matched, false otherwise
|
|
124
126
|
*/
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
139
|
-
|
|
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
|
-
|
|
145
|
-
const
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
176
|
-
|
|
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
|
-
|
|
187
|
-
const
|
|
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
|
-
|
|
190
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
*
|
|
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
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
-
*
|
|
636
|
+
* Process a node for state object extraction (single-pass helper)
|
|
637
|
+
* Returns true if node was matched, false otherwise
|
|
611
638
|
*/
|
|
612
|
-
function
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
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
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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:
|
|
655
|
-
functions
|
|
656
|
-
classes
|
|
657
|
-
stateObjects
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|