diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6494bf..7ebc3c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,3 +40,9 @@ jobs: run: npm run lint - name: Compile run: npm run compile + - name: Test (Linux) + if: runner.os == 'Linux' + run: xvfb-run -a npm run test + - name: Test (Windows/macOS) + if: runner.os != 'Linux' + run: npm run test diff --git a/README.md b/README.md index 747b962..d5d38b1 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,47 @@ When all options are enabled, methods are sorted in the following order: 5. **Lexical order** - Alphabetical ordering of method names 6. **Original position** - Maintains original order when all other criteria are equal +## Installation + +### From VS Code Marketplace +1. Open VS Code +2. Go to Extensions (`Ctrl+Shift+X`) +3. Search for "Translation" +4. Click Install + +### From VSIX File +1. Download the `.vsix` file from [Releases](https://github.com/tlcsdm/vscode-java-method-sorter/releases) +2. In VS Code, open Command Palette (`Ctrl+Shift+P`) +3. Search for "Extensions: Install from VSIX..." +4. Select the downloaded `.vsix` file + +### From Jenkins +Download from [Jenkins](https://jenkins.tlcsdm.com/job/vscode-plugin/job/vscode-java-method-sorter/) + +## Build + +This project uses TypeScript and npm (Node.js 22). + +```bash +# Install dependencies +npm install + +# Compile +npm run compile + +# Watch mode (for development) +npm run watch + +# Lint +npm run lint + +# Package +npx @vscode/vsce package + +# Test +npm run test +``` + ## Related Projects * [eclipse-method-sorter](https://github.com/tlcsdm/eclipse-method-sorter) diff --git a/src/sorter/javaMethodSorter.ts b/src/sorter/javaMethodSorter.ts index ac3bbf5..e364089 100644 --- a/src/sorter/javaMethodSorter.ts +++ b/src/sorter/javaMethodSorter.ts @@ -287,15 +287,23 @@ export class JavaMethodSorter { */ private reconstructSource(preContent: string, methods: JavaMethod[], postContent: string): string { const methodTexts = methods.map(m => { - // Combine leading content with method body - const leading = m.leadingContent.trim(); - if (leading) { - return leading + '\n' + m.fullText; + // Normalize fullText: remove leading newlines but preserve indentation + const normalizedFullText = m.fullText.replace(/^\n+/, ''); + + // Preserve leading content with indentation, only trim leading blank lines + // This keeps the comment/annotation indentation intact + const leading = m.leadingContent.replace(/^\n+/, ''); + if (leading.trim()) { + return leading + normalizedFullText; } - return m.fullText; + return normalizedFullText; }); - return preContent + methodTexts.join('\n\n') + postContent; + // Remove trailing whitespace from preContent to avoid duplicate indentation + // since leadingContent already includes the proper indentation + const normalizedPreContent = preContent.replace(/[ \t]+$/, ''); + + return normalizedPreContent + methodTexts.join('\n\n') + postContent; } /** diff --git a/src/test/sorter.test.ts b/src/test/sorter.test.ts index 5c971ec..c035692 100644 --- a/src/test/sorter.test.ts +++ b/src/test/sorter.test.ts @@ -345,6 +345,86 @@ public class MyClass { failed++; } + // Test 11: Comment indentation is preserved after sorting + try { + const source = `public class MyClass { + // 静态方法 + public static void log(String msg) { + System.out.println("LOG: " + msg); + } + + // 受保护方法 + protected void finalizeTask() { + System.out.println("Finalize"); + } +}`; + const options: SortingOptions = { + sortingStrategy: 'depth-first', + applyWorkingListHeuristics: false, + respectBeforeAfterRelation: false, + clusterOverloadedMethods: false, + clusterGetterSetter: false, + separateByAccessLevel: false, + separateConstructors: false, + applyLexicalOrdering: false + }; + const sorter = new JavaMethodSorter(options); + const sorted = sorter.sort(source); + + // Comments should maintain their indentation (4 spaces before //) + if (sorted.includes(' // 静态方法') && sorted.includes(' // 受保护方法')) { + console.log('✓ Test 11 passed: Comment indentation is preserved'); + passed++; + } else { + console.log('✗ Test 11 failed: Comment indentation not preserved'); + console.log('Sorted output:', sorted); + failed++; + } + } catch (e) { + console.log('✗ Test 11 failed with error:', e); + failed++; + } + + // Test 12: No double blank lines between methods after sorting + try { + const source = `public class MyClass { + protected void methodA() { + System.out.println("A"); + } + + private void methodB() { + System.out.println("B"); + } +}`; + const options: SortingOptions = { + sortingStrategy: 'depth-first', + applyWorkingListHeuristics: false, + respectBeforeAfterRelation: false, + clusterOverloadedMethods: false, + clusterGetterSetter: false, + separateByAccessLevel: true, + separateConstructors: false, + applyLexicalOrdering: false + }; + const sorter = new JavaMethodSorter(options); + const sorted = sorter.sort(source); + + // Should not have triple newlines (two blank lines) between methods + // Check that closing braces are followed by at most one blank line + const hasDoubleBlankLines = /\}\n\n\n/.test(sorted); + if (!hasDoubleBlankLines) { + console.log('✓ Test 12 passed: No double blank lines between methods'); + passed++; + } else { + console.log('✗ Test 12 failed: Found double blank lines between methods'); + console.log('Sorted output:', sorted); + failed++; + } + } catch (e) { + console.log('✗ Test 12 failed with error:', e); + failed++; + } + console.log(`\nResults: ${passed} passed, ${failed} failed`); }