diff --git a/src/services/tree-sitter/__tests__/fixtures/sample-java.ts b/src/services/tree-sitter/__tests__/fixtures/sample-java.ts index 80ecaf2da2..49ce65b821 100644 --- a/src/services/tree-sitter/__tests__/fixtures/sample-java.ts +++ b/src/services/tree-sitter/__tests__/fixtures/sample-java.ts @@ -29,16 +29,10 @@ public @interface TestAnnotationDefinition { // Interface declaration test - at least 4 lines long public interface TestInterfaceDefinition> { // Interface method declarations - void testInterfaceMethod( - String message, - T data - ); + void testInterfaceMethod(String message,T data); - // Default method in interface - 4+ lines - default String testInterfaceDefaultMethod( - String input, - T data - ) { + // Default method in interface + default String testInterfaceDefaultMethod(String input,T data) { return String.format("%s: %s", input, data.toString()); } } @@ -89,12 +83,17 @@ public class TestClassDefinition> instanceCount++; } - // Method implementation - at least 4 lines long + // Method implementation @Override - public void testInterfaceMethod( - String message, - T data - ) { + public void testInterfaceMethod(String message, T data) { + System.out.println(testInterfaceDefaultMethod(message, data)); + } + + @TestAnnotationDefinition( + value="test" + ) + + void testMultipleAnnotationMethod(String message, T data) { System.out.println(testInterfaceDefaultMethod(message, data)); } diff --git a/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts b/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts index 2a1291c2aa..1de55543f2 100644 --- a/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts +++ b/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts @@ -90,6 +90,8 @@ describe("parseSourceCodeDefinitionsForFile with Java", () => { it("should parse method declarations", () => { expect(parseResult).toMatch(/\d+--\d+ \|\s*void testInterfaceMethod\(/) + expect(parseResult).toMatch(/88--88 \|\s*public void testInterfaceMethod\(String message, T data\) {/) + expect(parseResult).toMatch(/\d+--\d+ \|\s*void testMultipleAnnotationMethod\(String message, T data\) {/) expect(parseResult).toMatch(/\d+--\d+ \|\s*default String testInterfaceDefaultMethod\(/) expect(parseResult).toMatch(/\d+--\d+ \|\s*public > R testGenericMethodDefinition\(/) expect(parseResult).toMatch(/\d+--\d+ \|\s*public String formatMessage\(/) diff --git a/src/services/tree-sitter/index.ts b/src/services/tree-sitter/index.ts index 145ba84730..68dd2366f7 100644 --- a/src/services/tree-sitter/index.ts +++ b/src/services/tree-sitter/index.ts @@ -7,6 +7,15 @@ import { parseMarkdown } from "./markdownParser" import { RooIgnoreController } from "../../core/ignore/RooIgnoreController" import { QueryCapture } from "web-tree-sitter" +type OutputItem = { + startLine: number + endLine: number + type: string + startContent: string +} + +const METHOD_CAPTURE = ["definition.method", "definition.method.start"] + // Private constant const DEFAULT_MIN_COMPONENT_LINES_VALUE = 4 @@ -27,6 +36,14 @@ export function setMinComponentLines(value: number): void { currentMinComponentLines = value } +function passesMinLines(lineCount: number, capture: QueryCapture, language: string) { + if (METHOD_CAPTURE.includes(capture.name)) { + // In object-oriented programming languages, method signatures are only one line and should not be ignored. + return false + } + return lineCount < getMinComponentLines() +} + const extensions = [ "tla", "js", @@ -262,7 +279,7 @@ This approach allows us to focus on the most relevant parts of the code (defined * * @param captures - The captures to process * @param lines - The lines of the file - * @param minComponentLines - Minimum number of lines for a component to be included + * @param language - The programming language of the file * @returns A formatted string with definitions */ function processCaptures(captures: QueryCapture[], lines: string[], language: string): string | null { @@ -283,7 +300,7 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st return null } - let formattedOutput = "" + const outputArr: OutputItem[] = [] // Sort captures by their start position captures.sort((a, b) => a.node.startPosition.row - b.node.startPosition.row) @@ -310,7 +327,7 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st const lineCount = endLine - startLine + 1 // Skip components that don't span enough lines - if (lineCount < getMinComponentLines()) { + if (passesMinLines(lineCount, capture, language)) { return } @@ -333,13 +350,23 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st // Add component name to output regardless of HTML filtering if (!processedLines.has(lineKey) && componentName) { - formattedOutput += `${startLine + 1}--${endLine + 1} | ${lines[startLine]}\n` + outputArr.push({ + startLine: startLine, + endLine: endLine, + type: name, + startContent: lines[startLine], + }) processedLines.add(lineKey) } } // For other component definitions else if (isNotHtmlElement(startLineContent)) { - formattedOutput += `${startLine + 1}--${endLine + 1} | ${lines[startLine]}\n` + outputArr.push({ + startLine: startLine, + endLine: endLine, + type: name, + startContent: lines[startLine], + }) processedLines.add(lineKey) // If this is part of a larger definition, include its non-HTML context @@ -352,7 +379,12 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st // Add the full range first const rangeKey = `${node.parent.startPosition.row}-${contextEnd}` if (!processedLines.has(rangeKey)) { - formattedOutput += `${node.parent.startPosition.row + 1}--${contextEnd + 1} | ${lines[node.parent.startPosition.row]}\n` + outputArr.push({ + startLine: node.parent.startPosition.row, + endLine: contextEnd, + type: name, + startContent: lines[node.parent.startPosition.row], + }) processedLines.add(rangeKey) } } @@ -360,8 +392,29 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st } }) - if (formattedOutput.length > 0) { - return formattedOutput + // Deduplication and filtering + const dedupedArr: OutputItem[] = [] + const seen = new Set() + for (const item of outputArr) { + // Only skip if there is another item with the same startLine and startContent + if (item.startLine === item.endLine && METHOD_CAPTURE.includes(item.type)) { + const isDuplicate = outputArr.some( + (other) => + other !== item && other.startLine === item.startLine && other.startContent === item.startContent, + ) + if (isDuplicate) { + continue + } + } + const key = `${item.startLine}-${item.endLine}-${item.type}-${item.startContent}` + if (!seen.has(key)) { + dedupedArr.push(item) + seen.add(key) + } + } + + if (dedupedArr.length > 0) { + return dedupedArr.map((item) => `${item.startLine + 1}--${item.endLine + 1} | ${item.startContent}`).join("\n") } return null diff --git a/src/services/tree-sitter/queries/java.ts b/src/services/tree-sitter/queries/java.ts index 63cb663e88..4a9a59835b 100644 --- a/src/services/tree-sitter/queries/java.ts +++ b/src/services/tree-sitter/queries/java.ts @@ -39,7 +39,9 @@ export default ` ; Method declarations (method_declaration - name: (identifier) @name.definition.method) @definition.method + type: (_) @definition.method.start + name: (identifier) @name.definition.method)@definition.method + ; Inner class declarations (class_declaration