From dafe76e9178738f0e42e476709ae2f4ee5d2e1cb Mon Sep 17 00:00:00 2001 From: NaccOll Date: Mon, 25 Aug 2025 01:47:25 +0800 Subject: [PATCH 1/7] feat: enhance Java method parsing when using listCodeDefinitionNamesTool. Fix #7330 --- .../__tests__/fixtures/sample-java.ts | 27 +++--- .../parseSourceCodeDefinitions.java.spec.ts | 4 + src/services/tree-sitter/index.ts | 97 ++++++++++++++++++- 3 files changed, 109 insertions(+), 19 deletions(-) diff --git a/src/services/tree-sitter/__tests__/fixtures/sample-java.ts b/src/services/tree-sitter/__tests__/fixtures/sample-java.ts index 80ecaf2da2..7f77b53fb2 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" + ) + + public 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..9635b77b2a 100644 --- a/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts +++ b/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts @@ -90,6 +90,10 @@ describe("parseSourceCodeDefinitionsForFile with Java", () => { it("should parse method declarations", () => { expect(parseResult).toMatch(/\d+--\d+ \|\s*void testInterfaceMethod\(/) + expect(parseResult).toMatch(/\d+--\d+ \|\s*public void testInterfaceMethod\(String message, T data\) {/) + expect(parseResult).toMatch( + /\d+--\d+ \|\s*public 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..a63ae25e16 100644 --- a/src/services/tree-sitter/index.ts +++ b/src/services/tree-sitter/index.ts @@ -262,7 +262,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 { @@ -310,7 +310,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 (shouldSkip(lineCount, capture, language)) { return } @@ -323,8 +323,14 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st return } + let outputLineIdx = startLine + // For Java method definitions, find the first non-annotation line + if (language === "java" && name === "definition.method") { + outputLineIdx = skipJavaAnnotations(lines, startLine, endLine) + } + // Check if this is a valid component definition (not an HTML element) - const startLineContent = lines[startLine].trim() + const startLineContent = lines[outputLineIdx].trim() // Special handling for component name definitions if (name.includes("name.definition")) { @@ -333,13 +339,13 @@ 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` + formattedOutput += `${outputLineIdx + 1}--${endLine + 1} | ${lines[outputLineIdx]}\n` processedLines.add(lineKey) } } // For other component definitions else if (isNotHtmlElement(startLineContent)) { - formattedOutput += `${startLine + 1}--${endLine + 1} | ${lines[startLine]}\n` + formattedOutput += `${outputLineIdx + 1}--${endLine + 1} | ${lines[outputLineIdx]}\n` processedLines.add(lineKey) // If this is part of a larger definition, include its non-HTML context @@ -413,3 +419,84 @@ async function parseFile( return null } } +function shouldSkip(lineCount: number, capture: QueryCapture, language: string) { + if (language === "java") { + if (["definition.method"].includes(capture.name)) { + return false + } + } + return lineCount < getMinComponentLines() +} + +/** + * Skip Java annotations and find the first line that contains the actual method declaration + * + * @param lines - Array of lines from the file + * @param startLine - Starting line index + * @param endLine - Ending line index + * @returns The line index of the first non-annotation line + */ +function skipJavaAnnotations(lines: string[], startLine: number, endLine: number): number { + let currentLine = startLine + let inAnnotation = false + let annotationDepth = 0 + + while (currentLine <= endLine) { + const line = lines[currentLine].trim() + + // Skip empty lines + if (line.length === 0) { + currentLine++ + continue + } + + // Check if this line starts an annotation + if (line.startsWith("@")) { + inAnnotation = true + annotationDepth = 0 + + // Count opening and closing parentheses to track annotation completion + for (const char of line) { + if (char === "(") { + annotationDepth++ + } else if (char === ")") { + annotationDepth-- + } + } + + // If annotation is complete on this line, mark as not in annotation + if (annotationDepth <= 0) { + inAnnotation = false + } + + currentLine++ + continue + } + + // If we're still inside a multi-line annotation + if (inAnnotation) { + // Count parentheses to track when annotation ends + for (const char of line) { + if (char === "(") { + annotationDepth++ + } else if (char === ")") { + annotationDepth-- + } + } + + // If annotation is complete, mark as not in annotation + if (annotationDepth <= 0) { + inAnnotation = false + } + + currentLine++ + continue + } + + // If we're not in an annotation and this line has content, this is our target + return currentLine + } + + // If we couldn't find a non-annotation line, return the start line + return startLine +} From e0150960260bf5ec90ae189ccab15d7d0825144a Mon Sep 17 00:00:00 2001 From: NaccOll Date: Mon, 25 Aug 2025 02:09:20 +0800 Subject: [PATCH 2/7] feat: Using regular expressions to optimize Java method signature matching --- src/services/tree-sitter/index.ts | 110 ++++++++++++------------------ 1 file changed, 43 insertions(+), 67 deletions(-) diff --git a/src/services/tree-sitter/index.ts b/src/services/tree-sitter/index.ts index a63ae25e16..0a38f77f83 100644 --- a/src/services/tree-sitter/index.ts +++ b/src/services/tree-sitter/index.ts @@ -10,6 +10,12 @@ import { QueryCapture } from "web-tree-sitter" // Private constant const DEFAULT_MIN_COMPONENT_LINES_VALUE = 4 +// Language-specific rules +const LANGUAGE_SKIP_RULES: Record = { + java: ["definition.method"], + // Future languages can be added here +} + // Getter function for MIN_COMPONENT_LINES (for easier testing) let currentMinComponentLines = DEFAULT_MIN_COMPONENT_LINES_VALUE @@ -27,6 +33,14 @@ export function setMinComponentLines(value: number): void { currentMinComponentLines = value } +function shouldSkipLineCountCheck(lineCount: number, capture: QueryCapture, language: string) { + const skipRules = LANGUAGE_SKIP_RULES[language] + if (skipRules && skipRules.includes(capture.name)) { + return false + } + return lineCount < getMinComponentLines() +} + const extensions = [ "tla", "js", @@ -310,7 +324,7 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st const lineCount = endLine - startLine + 1 // Skip components that don't span enough lines - if (shouldSkip(lineCount, capture, language)) { + if (shouldSkipLineCountCheck(lineCount, capture, language)) { return } @@ -324,9 +338,9 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st } let outputLineIdx = startLine - // For Java method definitions, find the first non-annotation line + // For Java method definitions, find the actual method signature line if (language === "java" && name === "definition.method") { - outputLineIdx = skipJavaAnnotations(lines, startLine, endLine) + outputLineIdx = findJavaMethodSignatureLine(lines, startLine, endLine) } // Check if this is a valid component definition (not an HTML element) @@ -419,84 +433,46 @@ async function parseFile( return null } } -function shouldSkip(lineCount: number, capture: QueryCapture, language: string) { - if (language === "java") { - if (["definition.method"].includes(capture.name)) { - return false - } - } - return lineCount < getMinComponentLines() -} /** - * Skip Java annotations and find the first line that contains the actual method declaration + * Find the line containing the actual Java method signature + * by looking for lines that match method declaration patterns * * @param lines - Array of lines from the file * @param startLine - Starting line index * @param endLine - Ending line index - * @returns The line index of the first non-annotation line + * @returns The line index of the method signature */ -function skipJavaAnnotations(lines: string[], startLine: number, endLine: number): number { - let currentLine = startLine - let inAnnotation = false - let annotationDepth = 0 - - while (currentLine <= endLine) { - const line = lines[currentLine].trim() - - // Skip empty lines - if (line.length === 0) { - currentLine++ +function findJavaMethodSignatureLine(lines: string[], startLine: number, endLine: number): number { + // Java method signature pattern: + // - May start with access modifiers (public, private, protected, static, final, etc.) + // - Followed by return type + // - Followed by method name + // - Followed by parentheses + const methodSignaturePattern = + /^\s*(public|private|protected|static|final|abstract|synchronized|native|strictfp|\w+\s+)*\w+\s*\(/ + + for (let i = startLine; i <= endLine; i++) { + const line = lines[i].trim() + + // Skip empty lines and annotation lines + if (line.length === 0 || line.startsWith("@")) { continue } - // Check if this line starts an annotation - if (line.startsWith("@")) { - inAnnotation = true - annotationDepth = 0 - - // Count opening and closing parentheses to track annotation completion - for (const char of line) { - if (char === "(") { - annotationDepth++ - } else if (char === ")") { - annotationDepth-- - } - } - - // If annotation is complete on this line, mark as not in annotation - if (annotationDepth <= 0) { - inAnnotation = false - } - - currentLine++ - continue + // Check if this line looks like a method signature + if (methodSignaturePattern.test(line)) { + return i } + } - // If we're still inside a multi-line annotation - if (inAnnotation) { - // Count parentheses to track when annotation ends - for (const char of line) { - if (char === "(") { - annotationDepth++ - } else if (char === ")") { - annotationDepth-- - } - } - - // If annotation is complete, mark as not in annotation - if (annotationDepth <= 0) { - inAnnotation = false - } - - currentLine++ - continue + // If no method signature found, return the first non-annotation, non-empty line + for (let i = startLine; i <= endLine; i++) { + const line = lines[i].trim() + if (line.length > 0 && !line.startsWith("@")) { + return i } - - // If we're not in an annotation and this line has content, this is our target - return currentLine } - // If we couldn't find a non-annotation line, return the start line return startLine } From a0be919f7f550d5345eddeb9ad1467d7e3384e62 Mon Sep 17 00:00:00 2001 From: NaccOll Date: Mon, 25 Aug 2025 02:18:08 +0800 Subject: [PATCH 3/7] fix: enhance regex for method signature matching --- src/services/tree-sitter/__tests__/fixtures/sample-java.ts | 2 +- .../__tests__/parseSourceCodeDefinitions.java.spec.ts | 4 +--- src/services/tree-sitter/index.ts | 3 ++- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/services/tree-sitter/__tests__/fixtures/sample-java.ts b/src/services/tree-sitter/__tests__/fixtures/sample-java.ts index 7f77b53fb2..49ce65b821 100644 --- a/src/services/tree-sitter/__tests__/fixtures/sample-java.ts +++ b/src/services/tree-sitter/__tests__/fixtures/sample-java.ts @@ -93,7 +93,7 @@ public class TestClassDefinition> value="test" ) - public void testMultipleAnnotationMethod(String message, T data) { + 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 9635b77b2a..b803275046 100644 --- a/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts +++ b/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts @@ -91,9 +91,7 @@ describe("parseSourceCodeDefinitionsForFile with Java", () => { it("should parse method declarations", () => { expect(parseResult).toMatch(/\d+--\d+ \|\s*void testInterfaceMethod\(/) expect(parseResult).toMatch(/\d+--\d+ \|\s*public void testInterfaceMethod\(String message, T data\) {/) - expect(parseResult).toMatch( - /\d+--\d+ \|\s*public void testMultipleAnnotationMethod\(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 0a38f77f83..3dfd2d535b 100644 --- a/src/services/tree-sitter/index.ts +++ b/src/services/tree-sitter/index.ts @@ -449,8 +449,9 @@ function findJavaMethodSignatureLine(lines: string[], startLine: number, endLine // - Followed by return type // - Followed by method name // - Followed by parentheses + // Using atomic groups and possessive quantifiers to prevent backtracking const methodSignaturePattern = - /^\s*(public|private|protected|static|final|abstract|synchronized|native|strictfp|\w+\s+)*\w+\s*\(/ + /^\s*(?:public|private|protected|static|final|abstract|synchronized|native|strictfp|\w+)(?:\s+(?:public|private|protected|static|final|abstract|synchronized|native|strictfp|\w+))*\s+\w+\s*\(/ for (let i = startLine; i <= endLine; i++) { const line = lines[i].trim() From 75670f3abb1664ba831b57aeef4e67178a45dcbe Mon Sep 17 00:00:00 2001 From: NaccOll Date: Mon, 25 Aug 2025 02:39:29 +0800 Subject: [PATCH 4/7] fix: simplify Java method signature detection to avoid backtracking issues --- src/services/tree-sitter/index.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/services/tree-sitter/index.ts b/src/services/tree-sitter/index.ts index 3dfd2d535b..ada8e6a8ea 100644 --- a/src/services/tree-sitter/index.ts +++ b/src/services/tree-sitter/index.ts @@ -444,14 +444,11 @@ async function parseFile( * @returns The line index of the method signature */ function findJavaMethodSignatureLine(lines: string[], startLine: number, endLine: number): number { - // Java method signature pattern: - // - May start with access modifiers (public, private, protected, static, final, etc.) - // - Followed by return type - // - Followed by method name - // - Followed by parentheses - // Using atomic groups and possessive quantifiers to prevent backtracking - const methodSignaturePattern = - /^\s*(?:public|private|protected|static|final|abstract|synchronized|native|strictfp|\w+)(?:\s+(?:public|private|protected|static|final|abstract|synchronized|native|strictfp|\w+))*\s+\w+\s*\(/ + // Java method signature pattern - avoiding backtracking issues + // Split into separate checks to avoid complex alternations + const accessModifiers = /^\s*(?:public|private|protected)\s+/ + const otherModifiers = /^\s*(?:static|final|abstract|synchronized|native|strictfp)\s+/ + const methodPattern = /^\s*(?:\w+(?:\[\])*(?:\s*<[^>]+>)?\s+)+\w+\s*\(/ for (let i = startLine; i <= endLine; i++) { const line = lines[i].trim() @@ -461,8 +458,9 @@ function findJavaMethodSignatureLine(lines: string[], startLine: number, endLine continue } - // Check if this line looks like a method signature - if (methodSignaturePattern.test(line)) { + // Check if this looks like a method signature using multiple simple patterns + // This avoids complex alternation that can cause backtracking + if (accessModifiers.test(line) || otherModifiers.test(line) || methodPattern.test(line)) { return i } } From c0dbbaf21f9011d2d33fcffea4d1459df78a5725 Mon Sep 17 00:00:00 2001 From: NaccOll Date: Thu, 28 Aug 2025 22:09:57 +0800 Subject: [PATCH 5/7] feat: enhance Java method declaration parsing to include type information --- src/services/tree-sitter/index.ts | 64 ++---------------------- src/services/tree-sitter/queries/java.ts | 5 ++ 2 files changed, 10 insertions(+), 59 deletions(-) diff --git a/src/services/tree-sitter/index.ts b/src/services/tree-sitter/index.ts index ada8e6a8ea..744de51a25 100644 --- a/src/services/tree-sitter/index.ts +++ b/src/services/tree-sitter/index.ts @@ -10,12 +10,6 @@ import { QueryCapture } from "web-tree-sitter" // Private constant const DEFAULT_MIN_COMPONENT_LINES_VALUE = 4 -// Language-specific rules -const LANGUAGE_SKIP_RULES: Record = { - java: ["definition.method"], - // Future languages can be added here -} - // Getter function for MIN_COMPONENT_LINES (for easier testing) let currentMinComponentLines = DEFAULT_MIN_COMPONENT_LINES_VALUE @@ -34,8 +28,8 @@ export function setMinComponentLines(value: number): void { } function shouldSkipLineCountCheck(lineCount: number, capture: QueryCapture, language: string) { - const skipRules = LANGUAGE_SKIP_RULES[language] - if (skipRules && skipRules.includes(capture.name)) { + if (["definition.method", "definition.method.start"].includes(capture.name)) { + // In object-oriented programming languages, method signatures are only one line and should not be ignored. return false } return lineCount < getMinComponentLines() @@ -337,14 +331,8 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st return } - let outputLineIdx = startLine - // For Java method definitions, find the actual method signature line - if (language === "java" && name === "definition.method") { - outputLineIdx = findJavaMethodSignatureLine(lines, startLine, endLine) - } - // Check if this is a valid component definition (not an HTML element) - const startLineContent = lines[outputLineIdx].trim() + const startLineContent = lines[startLine].trim() // Special handling for component name definitions if (name.includes("name.definition")) { @@ -353,13 +341,13 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st // Add component name to output regardless of HTML filtering if (!processedLines.has(lineKey) && componentName) { - formattedOutput += `${outputLineIdx + 1}--${endLine + 1} | ${lines[outputLineIdx]}\n` + formattedOutput += `${startLine + 1}--${endLine + 1} | ${lines[startLine]}\n` processedLines.add(lineKey) } } // For other component definitions else if (isNotHtmlElement(startLineContent)) { - formattedOutput += `${outputLineIdx + 1}--${endLine + 1} | ${lines[outputLineIdx]}\n` + formattedOutput += `${startLine + 1}--${endLine + 1} | ${lines[startLine]}\n` processedLines.add(lineKey) // If this is part of a larger definition, include its non-HTML context @@ -433,45 +421,3 @@ async function parseFile( return null } } - -/** - * Find the line containing the actual Java method signature - * by looking for lines that match method declaration patterns - * - * @param lines - Array of lines from the file - * @param startLine - Starting line index - * @param endLine - Ending line index - * @returns The line index of the method signature - */ -function findJavaMethodSignatureLine(lines: string[], startLine: number, endLine: number): number { - // Java method signature pattern - avoiding backtracking issues - // Split into separate checks to avoid complex alternations - const accessModifiers = /^\s*(?:public|private|protected)\s+/ - const otherModifiers = /^\s*(?:static|final|abstract|synchronized|native|strictfp)\s+/ - const methodPattern = /^\s*(?:\w+(?:\[\])*(?:\s*<[^>]+>)?\s+)+\w+\s*\(/ - - for (let i = startLine; i <= endLine; i++) { - const line = lines[i].trim() - - // Skip empty lines and annotation lines - if (line.length === 0 || line.startsWith("@")) { - continue - } - - // Check if this looks like a method signature using multiple simple patterns - // This avoids complex alternation that can cause backtracking - if (accessModifiers.test(line) || otherModifiers.test(line) || methodPattern.test(line)) { - return i - } - } - - // If no method signature found, return the first non-annotation, non-empty line - for (let i = startLine; i <= endLine; i++) { - const line = lines[i].trim() - if (line.length > 0 && !line.startsWith("@")) { - return i - } - } - - return startLine -} diff --git a/src/services/tree-sitter/queries/java.ts b/src/services/tree-sitter/queries/java.ts index 63cb663e88..1a884cecff 100644 --- a/src/services/tree-sitter/queries/java.ts +++ b/src/services/tree-sitter/queries/java.ts @@ -41,6 +41,11 @@ export default ` (method_declaration name: (identifier) @name.definition.method) @definition.method +; Method declarations with type - capture from type start +(method_declaration + type: (_) @definition.method.start + name: (identifier) @name.definition.method) + ; Inner class declarations (class_declaration (class_body From cdbba62e243a37de79708af400ee99f488069129 Mon Sep 17 00:00:00 2001 From: NaccOll Date: Fri, 5 Sep 2025 11:50:19 +0800 Subject: [PATCH 6/7] fix: update method declaration parsing to include body and parameters for better accuracy --- .../__tests__/parseSourceCodeDefinitions.java.spec.ts | 2 +- src/services/tree-sitter/index.ts | 4 ++-- src/services/tree-sitter/queries/java.ts | 7 ++----- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts b/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts index b803275046..1de55543f2 100644 --- a/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts +++ b/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.java.spec.ts @@ -90,7 +90,7 @@ describe("parseSourceCodeDefinitionsForFile with Java", () => { it("should parse method declarations", () => { expect(parseResult).toMatch(/\d+--\d+ \|\s*void testInterfaceMethod\(/) - expect(parseResult).toMatch(/\d+--\d+ \|\s*public void testInterfaceMethod\(String message, T data\) {/) + 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\(/) diff --git a/src/services/tree-sitter/index.ts b/src/services/tree-sitter/index.ts index 744de51a25..344991d08e 100644 --- a/src/services/tree-sitter/index.ts +++ b/src/services/tree-sitter/index.ts @@ -27,7 +27,7 @@ export function setMinComponentLines(value: number): void { currentMinComponentLines = value } -function shouldSkipLineCountCheck(lineCount: number, capture: QueryCapture, language: string) { +function passesMinLines(lineCount: number, capture: QueryCapture, language: string) { if (["definition.method", "definition.method.start"].includes(capture.name)) { // In object-oriented programming languages, method signatures are only one line and should not be ignored. return false @@ -318,7 +318,7 @@ function processCaptures(captures: QueryCapture[], lines: string[], language: st const lineCount = endLine - startLine + 1 // Skip components that don't span enough lines - if (shouldSkipLineCountCheck(lineCount, capture, language)) { + if (passesMinLines(lineCount, capture, language)) { return } diff --git a/src/services/tree-sitter/queries/java.ts b/src/services/tree-sitter/queries/java.ts index 1a884cecff..4a9a59835b 100644 --- a/src/services/tree-sitter/queries/java.ts +++ b/src/services/tree-sitter/queries/java.ts @@ -38,13 +38,10 @@ export default ` name: (identifier) @name.definition.constructor) @definition.constructor ; Method declarations -(method_declaration - name: (identifier) @name.definition.method) @definition.method - -; Method declarations with type - capture from type start (method_declaration type: (_) @definition.method.start - name: (identifier) @name.definition.method) + name: (identifier) @name.definition.method)@definition.method + ; Inner class declarations (class_declaration From 59a3dc9a231d3f9e6fafe0b351f47ed870e8a8c6 Mon Sep 17 00:00:00 2001 From: NaccOll Date: Fri, 5 Sep 2025 14:34:30 +0800 Subject: [PATCH 7/7] feat: remove deduplication method define --- src/services/tree-sitter/index.ts | 59 +++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/src/services/tree-sitter/index.ts b/src/services/tree-sitter/index.ts index 344991d08e..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 @@ -28,7 +37,7 @@ export function setMinComponentLines(value: number): void { } function passesMinLines(lineCount: number, capture: QueryCapture, language: string) { - if (["definition.method", "definition.method.start"].includes(capture.name)) { + if (METHOD_CAPTURE.includes(capture.name)) { // In object-oriented programming languages, method signatures are only one line and should not be ignored. return false } @@ -291,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) @@ -341,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 @@ -360,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) } } @@ -368,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