Skip to content

Commit dafe76e

Browse files
committed
feat: enhance Java method parsing when using listCodeDefinitionNamesTool. Fix #7330
1 parent 90e7d09 commit dafe76e

File tree

3 files changed

+109
-19
lines changed

3 files changed

+109
-19
lines changed

src/services/tree-sitter/__tests__/fixtures/sample-java.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,10 @@ public @interface TestAnnotationDefinition {
2929
// Interface declaration test - at least 4 lines long
3030
public interface TestInterfaceDefinition<T extends Comparable<T>> {
3131
// Interface method declarations
32-
void testInterfaceMethod(
33-
String message,
34-
T data
35-
);
32+
void testInterfaceMethod(String message,T data);
3633
37-
// Default method in interface - 4+ lines
38-
default String testInterfaceDefaultMethod(
39-
String input,
40-
T data
41-
) {
34+
// Default method in interface
35+
default String testInterfaceDefaultMethod(String input,T data) {
4236
return String.format("%s: %s", input, data.toString());
4337
}
4438
}
@@ -89,12 +83,17 @@ public class TestClassDefinition<T extends Comparable<T>>
8983
instanceCount++;
9084
}
9185
92-
// Method implementation - at least 4 lines long
86+
// Method implementation
9387
@Override
94-
public void testInterfaceMethod(
95-
String message,
96-
T data
97-
) {
88+
public void testInterfaceMethod(String message, T data) {
89+
System.out.println(testInterfaceDefaultMethod(message, data));
90+
}
91+
92+
@TestAnnotationDefinition(
93+
value="test"
94+
)
95+
96+
public void testMultipleAnnotationMethod(String message, T data) {
9897
System.out.println(testInterfaceDefaultMethod(message, data));
9998
}
10099

src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ describe("parseSourceCodeDefinitionsForFile with Java", () => {
9090

9191
it("should parse method declarations", () => {
9292
expect(parseResult).toMatch(/\d+--\d+ \|\s*void testInterfaceMethod\(/)
93+
expect(parseResult).toMatch(/\d+--\d+ \|\s*public void testInterfaceMethod\(String message, T data\) {/)
94+
expect(parseResult).toMatch(
95+
/\d+--\d+ \|\s*public void testMultipleAnnotationMethod\(String message, T data\) {/,
96+
)
9397
expect(parseResult).toMatch(/\d+--\d+ \|\s*default String testInterfaceDefaultMethod\(/)
9498
expect(parseResult).toMatch(/\d+--\d+ \|\s*public <R extends Comparable<R>> R testGenericMethodDefinition\(/)
9599
expect(parseResult).toMatch(/\d+--\d+ \|\s*public String formatMessage\(/)

src/services/tree-sitter/index.ts

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ This approach allows us to focus on the most relevant parts of the code (defined
262262
*
263263
* @param captures - The captures to process
264264
* @param lines - The lines of the file
265-
* @param minComponentLines - Minimum number of lines for a component to be included
265+
* @param language - The programming language of the file
266266
* @returns A formatted string with definitions
267267
*/
268268
function processCaptures(captures: QueryCapture[], lines: string[], language: string): string | null {
@@ -310,7 +310,7 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st
310310
const lineCount = endLine - startLine + 1
311311

312312
// Skip components that don't span enough lines
313-
if (lineCount < getMinComponentLines()) {
313+
if (shouldSkip(lineCount, capture, language)) {
314314
return
315315
}
316316

@@ -323,8 +323,14 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st
323323
return
324324
}
325325

326+
let outputLineIdx = startLine
327+
// For Java method definitions, find the first non-annotation line
328+
if (language === "java" && name === "definition.method") {
329+
outputLineIdx = skipJavaAnnotations(lines, startLine, endLine)
330+
}
331+
326332
// Check if this is a valid component definition (not an HTML element)
327-
const startLineContent = lines[startLine].trim()
333+
const startLineContent = lines[outputLineIdx].trim()
328334

329335
// Special handling for component name definitions
330336
if (name.includes("name.definition")) {
@@ -333,13 +339,13 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st
333339

334340
// Add component name to output regardless of HTML filtering
335341
if (!processedLines.has(lineKey) && componentName) {
336-
formattedOutput += `${startLine + 1}--${endLine + 1} | ${lines[startLine]}\n`
342+
formattedOutput += `${outputLineIdx + 1}--${endLine + 1} | ${lines[outputLineIdx]}\n`
337343
processedLines.add(lineKey)
338344
}
339345
}
340346
// For other component definitions
341347
else if (isNotHtmlElement(startLineContent)) {
342-
formattedOutput += `${startLine + 1}--${endLine + 1} | ${lines[startLine]}\n`
348+
formattedOutput += `${outputLineIdx + 1}--${endLine + 1} | ${lines[outputLineIdx]}\n`
343349
processedLines.add(lineKey)
344350

345351
// If this is part of a larger definition, include its non-HTML context
@@ -413,3 +419,84 @@ async function parseFile(
413419
return null
414420
}
415421
}
422+
function shouldSkip(lineCount: number, capture: QueryCapture, language: string) {
423+
if (language === "java") {
424+
if (["definition.method"].includes(capture.name)) {
425+
return false
426+
}
427+
}
428+
return lineCount < getMinComponentLines()
429+
}
430+
431+
/**
432+
* Skip Java annotations and find the first line that contains the actual method declaration
433+
*
434+
* @param lines - Array of lines from the file
435+
* @param startLine - Starting line index
436+
* @param endLine - Ending line index
437+
* @returns The line index of the first non-annotation line
438+
*/
439+
function skipJavaAnnotations(lines: string[], startLine: number, endLine: number): number {
440+
let currentLine = startLine
441+
let inAnnotation = false
442+
let annotationDepth = 0
443+
444+
while (currentLine <= endLine) {
445+
const line = lines[currentLine].trim()
446+
447+
// Skip empty lines
448+
if (line.length === 0) {
449+
currentLine++
450+
continue
451+
}
452+
453+
// Check if this line starts an annotation
454+
if (line.startsWith("@")) {
455+
inAnnotation = true
456+
annotationDepth = 0
457+
458+
// Count opening and closing parentheses to track annotation completion
459+
for (const char of line) {
460+
if (char === "(") {
461+
annotationDepth++
462+
} else if (char === ")") {
463+
annotationDepth--
464+
}
465+
}
466+
467+
// If annotation is complete on this line, mark as not in annotation
468+
if (annotationDepth <= 0) {
469+
inAnnotation = false
470+
}
471+
472+
currentLine++
473+
continue
474+
}
475+
476+
// If we're still inside a multi-line annotation
477+
if (inAnnotation) {
478+
// Count parentheses to track when annotation ends
479+
for (const char of line) {
480+
if (char === "(") {
481+
annotationDepth++
482+
} else if (char === ")") {
483+
annotationDepth--
484+
}
485+
}
486+
487+
// If annotation is complete, mark as not in annotation
488+
if (annotationDepth <= 0) {
489+
inAnnotation = false
490+
}
491+
492+
currentLine++
493+
continue
494+
}
495+
496+
// If we're not in an annotation and this line has content, this is our target
497+
return currentLine
498+
}
499+
500+
// If we couldn't find a non-annotation line, return the start line
501+
return startLine
502+
}

0 commit comments

Comments
 (0)