├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── gifs │ ├── demo.gif │ ├── liveLinting.gif │ ├── setConfiguration.gif │ └── setVersion(lower).gif └── imgs │ ├── btn_bulb.png │ ├── quick_fix.png │ ├── set_config.png │ └── set_version.png ├── javaConfig.json ├── jdtls.ext ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── com.shengchen.checkstyle.checker │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── shengchen │ │ └── checkstyle │ │ └── checker │ │ ├── CheckerListener.java │ │ └── CheckerService.java ├── com.shengchen.checkstyle.runner │ ├── .classpath │ ├── .project │ ├── .settings │ │ ├── org.eclipse.core.resources.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── META-INF │ │ └── MANIFEST.MF │ ├── build.properties │ ├── plugin.xml │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── shengchen │ │ └── checkstyle │ │ ├── quickfix │ │ ├── BaseQuickFix.java │ │ ├── FixableCheck.java │ │ ├── QuickFixService.java │ │ ├── blocks │ │ │ ├── AvoidNestedBlocksQuickFix.java │ │ │ └── NeedBracesQuickFix.java │ │ ├── coding │ │ │ ├── DefaultComesLastQuickFix.java │ │ │ ├── EmptyStatementQuickFix.java │ │ │ ├── ExplicitInitializationQuickFix.java │ │ │ ├── FinalLocalVariableQuickFix.java │ │ │ ├── MissingSwitchDefaultQuickFix.java │ │ │ ├── MultipleVariableDeclarationsQuickFix.java │ │ │ ├── RequireThisQuickFix.java │ │ │ ├── SimplifyBooleanReturnQuickFix.java │ │ │ └── StringLiteralEqualityQuickFix.java │ │ ├── design │ │ │ ├── DesignForExtensionQuickFix.java │ │ │ └── FinalClassQuickFix.java │ │ ├── misc │ │ │ ├── ArrayTypeStyleQuickFix.java │ │ │ ├── FinalParametersQuickFix.java │ │ │ ├── UncommentedMainQuickFix.java │ │ │ └── UpperEllQuickFix.java │ │ ├── modifier │ │ │ ├── ModifierOrderQuickFix.java │ │ │ └── RedundantModifierQuickFix.java │ │ └── utils │ │ │ └── EditUtils.java │ │ └── runner │ │ ├── CheckstyleLoader.java │ │ ├── CheckstylePlugin.java │ │ ├── DelegateCommandHandler.java │ │ └── api │ │ ├── CheckResult.java │ │ ├── ICheckerService.java │ │ └── IQuickFixService.java ├── com.shengchen.checkstyle.target │ ├── pom.xml │ └── target.target ├── config │ ├── checkstyle.xml │ └── java.header ├── mvnw ├── mvnw.cmd └── pom.xml ├── package-lock.json ├── package.json ├── package.nls.json ├── package.nls.zh.json ├── resources └── icon_checkstyle.png ├── scripts └── build.js ├── src ├── checkstyleChannel.ts ├── checkstyleConfigurationManager.ts ├── checkstyleDiagnosticCollector.ts ├── checkstyleDiagnosticManager.ts ├── checkstyleStatusBar.ts ├── commands │ ├── check.ts │ ├── config.ts │ ├── executeJavaLanguageServerCommand.ts │ ├── fix.ts │ └── version.ts ├── constants │ ├── checkstyleConfigs.ts │ ├── commands.ts │ ├── quickFix.ts │ └── settings.ts ├── extension.ts ├── features │ └── FileSynchronizer.ts ├── models.ts ├── quickFixProvider.ts └── utils │ ├── editUtils.ts │ ├── errorUtils.ts │ ├── quickFixUtils.ts │ ├── settingUtils.ts │ └── workspaceUtils.ts ├── thirdpartynotices.txt ├── tsconfig.json └── webpack.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["eslint:recommended"], 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 2018, 6 | "sourceType": "module", 7 | "project": "./tsconfig.json" 8 | }, 9 | "env": { 10 | "node": true 11 | }, 12 | "plugins": ["@typescript-eslint"], 13 | "rules": { 14 | "sort-keys": 0, 15 | "indent": ["error", 2], 16 | "no-inner-declarations": "off", 17 | "max-len": ["off", 120], 18 | "quotes": ["error", "single", { "avoidEscape": true }], 19 | "camelcase": "off" 20 | }, 21 | "overrides": [ 22 | { 23 | "files": ["*.ts", "*.tsx"], 24 | "extends": ["plugin:@typescript-eslint/recommended"], 25 | "rules": { 26 | "@typescript-eslint/no-namespace": "off", 27 | "@typescript-eslint/typedef": [ 28 | "warn", 29 | { 30 | "arrowParameter": true, 31 | "variableDeclaration": true, 32 | "memberVariableDeclaration": true 33 | } 34 | ], 35 | "@typescript-eslint/ban-types": "off", 36 | "@typescript-eslint/no-unused-vars": "warn", 37 | "@typescript-eslint/no-explicit-any": "warn", 38 | "@typescript-eslint/naming-convention": "off" 39 | } 40 | } 41 | ], 42 | "settings": { 43 | "import/parsers": { 44 | "@typescript-eslint/parser": [".ts", ".tsx"] 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.java text eol=lf 4 | *.js text eol=lf 5 | *.json text eol=lf 6 | *.ts text eol=lf 7 | *.md text eol=lf 8 | *.MF text eol=lf 9 | *.prefs text eol=lf 10 | *.target text eol=lf 11 | *.txt text eol=lf 12 | */.classpath text eol=lf 13 | */.project text eol=lf 14 | *.yml text eol=lf 15 | 16 | *.gif binary 17 | *.jar binary 18 | *.png binary -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | linux: 11 | name: Linux 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 30 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Setup Build Environment 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 21 | sudo /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & 22 | sleep 3 23 | - name: Set up JDK 17 24 | uses: actions/setup-java@v1 25 | with: 26 | java-version: "17" 27 | 28 | - name: Setup Node.js environment 29 | uses: actions/setup-node@v2 30 | with: 31 | node-version: 18 32 | 33 | - name: Install Node.js modules 34 | run: npm install 35 | 36 | - name: Lint 37 | run: npm run lint 38 | 39 | - name: Build OSGi bundle 40 | run: npm run build-plugin 41 | 42 | - name: Build .vsix 43 | run: npx vsce@latest package 44 | 45 | windows: 46 | name: Windows 47 | runs-on: windows-latest 48 | timeout-minutes: 30 49 | steps: 50 | - uses: actions/checkout@v2 51 | 52 | - name: Set up JDK 17 53 | uses: actions/setup-java@v1 54 | with: 55 | java-version: "17" 56 | 57 | - name: Setup Node.js environment 58 | uses: actions/setup-node@v2 59 | with: 60 | node-version: 18 61 | 62 | - name: Install Node.js modules 63 | run: npm install 64 | 65 | - name: Lint 66 | run: npm run lint 67 | 68 | - name: Build OSGi bundle 69 | run: npm run build-plugin 70 | 71 | - name: Build .vsix 72 | run: npx vsce@latest package 73 | 74 | darwin: 75 | name: macOS 76 | runs-on: macos-latest 77 | timeout-minutes: 30 78 | steps: 79 | - uses: actions/checkout@v2 80 | 81 | - name: Set up JDK 17 82 | uses: actions/setup-java@v1 83 | with: 84 | java-version: "17" 85 | 86 | - name: Setup Node.js environment 87 | uses: actions/setup-node@v2 88 | with: 89 | node-version: 18 90 | 91 | - name: Install Node.js modules 92 | run: npm install 93 | 94 | - name: Lint 95 | run: npm run lint 96 | 97 | - name: Build OSGi bundle 98 | run: npm run build-plugin 99 | 100 | - name: Build .vsix 101 | run: npx vsce@latest package 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | **/test-launch.config 7 | 8 | # BlueJ files 9 | *.ctxt 10 | 11 | # Mobile Tools for Java (J2ME) 12 | .mtj.tmp/ 13 | 14 | # Package Files # 15 | *.jar 16 | *.war 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | *.iml 22 | **/*.vsix 23 | 24 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 25 | hs_err_pid* 26 | 27 | # build 28 | target/ 29 | 30 | # Checkstyle 31 | **/.checkstyle 32 | 33 | # VS Code 34 | out 35 | **/node_modules/ 36 | .vscode-test/ 37 | *.vsix 38 | dist 39 | jdtls/ 40 | 41 | # Mac 42 | .DS_Store 43 | 44 | !jdtls.ext/.mvn/wrapper/maven-wrapper.jar 45 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "yaozheng.vscode-pde", 6 | "dbaeumer.vscode-eslint", 7 | "rvest.vs-code-prettier-eslint", 8 | "amodio.tsl-problem-matcher" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Launch Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"], 14 | "sourceMaps": true, 15 | "outFiles": ["${workspaceRoot}/dist/**/*.js"], 16 | "preLaunchTask": "npm: watch" 17 | }, 18 | { 19 | "name": "Extension Tests", 20 | "type": "extensionHost", 21 | "request": "launch", 22 | "runtimeExecutable": "${execPath}", 23 | "args": [ 24 | "--extensionDevelopmentPath=${workspaceFolder}", 25 | "--extensionTestsPath=${workspaceFolder}/out/test" 26 | ], 27 | "outFiles": ["${workspaceFolder}/out/test/**/*.js"], 28 | "preLaunchTask": "npm: watch" 29 | }, 30 | { 31 | "projectName": "com.shengchen.checkstyle.runner", 32 | "type": "java", 33 | "name": "Attach to Checkstyle Plugin", 34 | "request": "attach", 35 | "hostName": "localhost", 36 | "port": 1044 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off", 11 | // Use workspace's typescript version to provide correct language service 12 | "typescript.tsdk": "node_modules/typescript/lib", 13 | "java.checkstyle.configuration": "${workspaceFolder}/jdtls.ext/config/checkstyle.xml", 14 | "java.checkstyle.properties": { 15 | "checkstyle.header.file": "${workspaceFolder}/jdtls.ext/config/java.header" 16 | }, 17 | "java.configuration.updateBuildConfiguration": "automatic", 18 | "files.eol": "\n", 19 | "editor.formatOnType": false, 20 | "editor.formatOnPaste": true, 21 | "editor.formatOnSave": true, 22 | "editor.formatOnSaveMode": "file", 23 | "editor.tabSize": 2, 24 | "[java]": { 25 | "editor.tabSize": 4, 26 | "editor.defaultFormatter": "redhat.java" 27 | }, 28 | "[json]": { 29 | "editor.defaultFormatter": "rvest.vs-code-prettier-eslint" 30 | }, 31 | "[ts]": { 32 | "editor.defaultFormatter": "rvest.vs-code-prettier-eslint" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "watch", 7 | "problemMatcher": ["$ts-webpack-watch", "$tslint-webpack-watch"], 8 | "isBackground": true, 9 | "presentation": { 10 | "reveal": "never" 11 | }, 12 | "group": { 13 | "kind": "build", 14 | "isDefault": true 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | **/tsconfig.json 7 | **/tslint.json 8 | **/*.map 9 | **/*.ts 10 | dist/**/*.map 11 | webpack.config.js 12 | node_modules 13 | package-lock.json 14 | docs 15 | gulpfile.js 16 | jdtls.ext 17 | .github/** 18 | javaConfig.json 19 | scripts/** -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to the "vscode-checkstyle" extension will be documented in this file. 3 | 4 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 5 | 6 | ## [1.4.2] 7 | ### Changed 8 | - Update the default Checkstyle version to `9.3`. 9 | ### Fixed 10 | - Does not work on VS Code start until setting.json modified. [#341](https://github.com/jdneo/vscode-checkstyle/issues/341) 11 | 12 | ## [1.4.1] 13 | ### Fixed 14 | - Generate temp files according to the folder structure [#275](https://github.com/jdneo/vscode-checkstyle/issues/275) 15 | - Remove redundant modifiers on interface declarations [PR#302](https://github.com/jdneo/vscode-checkstyle/pull/302) 16 | - Fix MultipleVariableDeclarationsQuickFix comment handling [PR#304](https://github.com/jdneo/vscode-checkstyle/pull/304) 17 | - Fix compatibility for Checkstyle v8.42+ [PR#323](https://github.com/jdneo/vscode-checkstyle/pull/323) 18 | 19 | ## [1.4.0] 20 | ### Added 21 | - Support quick fix for [MultipleVariableDeclarations](https://checkstyle.sourceforge.io/config_coding.html#MultipleVariableDeclarations) check ([PR#290](https://github.com/jdneo/vscode-checkstyle/pull/290)) 22 | - Support quick fix to fix all Checkstyle violations ([#179](https://github.com/jdneo/vscode-checkstyle/issues/179)) 23 | 24 | ### Fixed 25 | - [Bugs fixed](https://github.com/jdneo/vscode-checkstyle/issues?q=is%3Aissue+is%3Aclosed+milestone%3A1.4.0+label%3Abug) 26 | 27 | ## [1.3.3] 28 | ### Changed 29 | - Update the dependencies. 30 | 31 | ## [1.3.2] 32 | ### Added 33 | - Support customized Checkstyle modules ([#206](https://github.com/jdneo/vscode-checkstyle/issues/206)) 34 | 35 | ## [1.3.1] 36 | ### Fixed 37 | - [Bugs fixed](https://github.com/jdneo/vscode-checkstyle/issues?q=is%3Aissue+is%3Aclosed+milestone%3A1.3.1+label%3Abug) 38 | 39 | ## [1.3.0] 40 | ### Added 41 | - User can manually set the Checkstyle version used for the project. ([#172](https://github.com/jdneo/vscode-checkstyle/issues/172)) 42 | 43 | ### Fixed 44 | - [Bugs fixed](https://github.com/jdneo/vscode-checkstyle/issues?q=is%3Aissue+is%3Aclosed+milestone%3A1.3.0+label%3Abug) 45 | 46 | ## [1.2.0] 47 | ### Added 48 | - Open the Problems panel when click the status icon in the status bar. ([#176](https://github.com/jdneo/vscode-checkstyle/issues/176)) 49 | 50 | ### Changed 51 | - Automatically detect potential Checkstyle configuration files when using command to set the configuration. ([#PR215](https://github.com/jdneo/vscode-checkstyle/pull/215)) 52 | 53 | ### Fixed 54 | - [Bugs fixed](https://github.com/jdneo/vscode-checkstyle/issues?q=is%3Aissue+is%3Aclosed+milestone%3A1.2.0+label%3Abug) 55 | 56 | ## [1.1.0] 57 | ### Added 58 | - Support the live linting experience. Now the users do **not** need to save the file to refresh the linting results. ([#175](https://github.com/jdneo/vscode-checkstyle/issues/175)) 59 | - Support batch check. ([#178](https://github.com/jdneo/vscode-checkstyle/issues/178)) 60 | 61 | ## [1.0.3] 62 | ### Added 63 | - Support loading CheckStyle Configuration via URL. ([#181](https://github.com/jdneo/vscode-checkstyle/issues/181)) 64 | 65 | ### Fixed 66 | - Fix vulnerability issues. ([PR#170](https://github.com/jdneo/vscode-checkstyle/pull/170)) 67 | 68 | ## [1.0.2] 69 | ### Fixed 70 | - Fixed the java extension version. 71 | 72 | ## [1.0.1] 73 | ### Changed 74 | - Upgrade the embedded Checkstyle version to 8.18. 75 | 76 | ### Fixed 77 | - [Bug fixed](https://github.com/jdneo/vscode-checkstyle/issues?q=is%3Aissue+is%3Aclosed+milestone%3A1.0.1+label%3Abug) 78 | 79 | ### [1.0.0] 80 | Initial release for the new Checkstyle extension, the new extension contains following features: 81 | - Check code with Checkstyle. 82 | - Provide quick fix if it's available. 83 | 84 | ## [0.5.2] 85 | ### Fixed 86 | - Fixed some bugs. 87 | 88 | ## [0.5.1] 89 | ### Fixed 90 | - Disable Checkstyle commands appear in the context menu of output panel. ([#118](https://github.com/jdneo/vscode-checkstyle/issues/118)) 91 | - Download Checkstyle jars from Github. ([#120](https://github.com/jdneo/vscode-checkstyle/issues/120)) 92 | 93 | ## [0.5.0] 94 | ### Added 95 | - Support clean Checkstyle Violation through Command Palette/File Explorer/Editor. ([#104](https://github.com/jdneo/vscode-checkstyle/issues/104)) 96 | - Support check code through File Explorer/Editor. ([#108](https://github.com/jdneo/vscode-checkstyle/issues/108)) 97 | 98 | ## [0.4.1] 99 | ### Fixed 100 | - Fix a potential security vulnerability. ([#100](https://github.com/jdneo/vscode-checkstyle/issues/100)) 101 | 102 | ## [0.4.0] 103 | ### Added 104 | - Add 'don't warn again' option when invalid CheckStyle version warning pops up. ([#80](https://github.com/jdneo/vscode-checkstyle/pull/80)) 105 | 106 | ### Changed 107 | - Change status bar icon functionalities. ([#78](https://github.com/jdneo/vscode-checkstyle/pull/78)) 108 | - User can open the Checkstyle download page when downloading fails. ([#79](https://github.com/jdneo/vscode-checkstyle/pull/79)) 109 | - Change command name. ([#93](https://github.com/jdneo/vscode-checkstyle/pull/93)) 110 | 111 | ## [0.3.1] 112 | ### Changed 113 | - Won't open the output channel automatically when error occurs. ([#74](https://github.com/jdneo/vscode-checkstyle/issues/74)) 114 | 115 | ### Fixed 116 | - Fix the property resolve bug. ([#75](https://github.com/jdneo/vscode-checkstyle/issues/75)) 117 | 118 | ## [0.3.0] 119 | ### Added 120 | - Add support to automatically resolve properties in the Checkstyle configuration file. 121 | 122 | ### Fixed 123 | - Fix argument contains whitespace error. ([#61](https://github.com/jdneo/vscode-checkstyle/issues/61)) 124 | 125 | ## [0.2.0] 126 | ### Added 127 | - Add support to automatically resolve and download Checkstyle. 128 | - Add status icon in status bar. 129 | - Add setting autocheck command into command palette. 130 | - Add setting property file command into command palette. 131 | 132 | ### Changed 133 | - Turn off ```autocheck``` by default. 134 | - User Cancel Action will not pop up error message any more. 135 | 136 | ## [0.1.1] 137 | ### Fixed 138 | - Fix line must be positive error. ([#31](https://github.com/jdneo/vscode-checkstyle/issues/31)) 139 | 140 | ## [0.1.0] 141 | ### Added 142 | - Add support changing Checkstyle jar file and configuration through commands. 143 | - Add the ability to download Checkstyle 8.0 for the first time run. 144 | - Add setting ```checkstyle.autocheck```. 145 | 146 | ### Fixed 147 | - Fix the issue that the checkstyle output may not be correctly parsed. 148 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | Thank you for your interest in contributing to this extension! 4 | 5 | There are many ways in which you can contribute, beyond writing code. Please read the following document to check how you can get involved. 6 | 7 | ## Questions and Feedback 8 | Have questions or feedback? Feel free to let us know! You can share your thoughts in our Gitter channel: [![Gitter](https://badges.gitter.im/jdneo/vscode-checkstyle.svg)](https://gitter.im/vscode-checkstyle/Lobby) 9 | 10 | ## Reporting Issues 11 | You can report issues whenever: 12 | - Identify a reproducible problem within the extension 13 | - Have a feature request 14 | 15 | ### Looking for an Existing Issue 16 | Before creating a new issue, please do a search to see if the issue or feature request has already been filed. 17 | 18 | If you find your issue already exists, make relevant comments and add your reaction: 19 | - 👍 - upvote 20 | - 👎 - downvote 21 | 22 | ### Writing Good Bug Reports and Feature Requests 23 | In order to let us know better about the issue, please make sure the following items are included with each issue: 24 | - The version of VS Code 25 | - Your operating system 26 | - Reproducible steps 27 | - What you expected to see, versus what you actually saw 28 | - Images, animations, or a link to a video showing the issue occurring 29 | - A code snippet that demonstrates the issue or a link to a code repository the developers can easily pull down to recreate the issue locally 30 | - Errors from the Dev Tools Console (open from the menu: Help > Toggle Developer Tools) 31 | 32 | ## Contributing Fixes 33 | If you are interested in writing code to fix issues, please check the following content to see how to set up the developing environment. 34 | 35 | ### Overview 36 | The extension has three major modules, which are listed as follow: 37 | - The extension client written in TypeScript - UI logic mostly 38 | - [The Checkstyle checker](https://github.com/jdneo/vscode-checkstyle/tree/master/jdtls.ext/com.shengchen.checkstyle.checker) written in Java - Interact with the Checkstyle's tooling API. 39 | - [The Checkstyle runner](https://github.com/jdneo/vscode-checkstyle/tree/master/jdtls.ext/com.shengchen.checkstyle.runner) written in Java - The OSGi bundle loaded into the Java Language Server and interact with the extension client. 40 | 41 | ### Setup 42 | 0. Make sure you have latest LTS JDK, Node.js, VS Code and [Java Extension Pack](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack) installed. 43 | 1. Fork and clone the repository: `git clone https://github.com/jdneo/vscode-checkstyle.git`. 44 | 2. `cd vscode-checkstyle`. 45 | 3. Install the node dependencies: `npm install`. 46 | 4. Build the Java modules: `npm run build-plugin`. 47 | 6. Open the directory `vscode-checkstyle` in VS Code. 48 | 7. Install the [Eclipse PDE Support extension](https://marketplace.visualstudio.com/items?itemName=yaozheng.vscode-pde) in your VS Code. 49 | 8. Install the [TypeScript + Webpack Problem Matchers](https://marketplace.visualstudio.com/items?itemName=amodio.tsl-problem-matcher) in your VS Code. 50 | 9. Open a Java file and wait until 👍 shows in the right-bottom of the status bar 51 | > Note: Sometimes, if you find the code navigation is not working in the Java code, please try: 52 | > - right click the [target.target](https://github.com/jdneo/vscode-checkstyle/blob/master/jdtls.ext/com.shengchen.checkstyle.target/target.target) file and select `Reload Target Platform`. 53 | > - Reload your VS Code. 54 | 55 | ### Debugging 56 | 1. Hit `F5` (or run `Launch Extension` in the debug viewlet) to launch the extension in debug mode 57 | > This will open a new VS Code window as a debug session. Open a Java project folder and let the extension be activated, then you can debug it. 58 | 2. If you want to debug the Checkstyle runner, run [Attach to Checkstyle Plugin](https://github.com/jdneo/vscode-checkstyle/blob/master/.vscode/launch.json) in the debug viewlet. 59 | 60 | > Note: If the Java code is changed by you, please run `npm run build-plugin` before you start debugging, the output jars will be generated in the folder `server/`. Or you can use the [HCR](https://code.visualstudio.com/docs/java/java-debugging#_hot-code-replacement) feature provided by the VS Code Java Debugger extension. 61 | 62 | ### Build Your Own Private Build 63 | If you want to build and install your own private build: 64 | 65 | ```shell 66 | npm run build-plugin 67 | npx vsce@latest package 68 | code --install-extension vscode-checkstyle-*.vsix 69 | ``` 70 | 71 | ### Check Linting Errors: 72 | Run `npm run lint` to check linting errors. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Checkstyle for VS Code 2 | 3 | > Check your Java code format and fix it! 4 | 5 |

6 | 7 |

8 |

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |

19 | 20 | ## Requirements 21 | - JDK (version 17 or later) 22 | - VS Code (version 1.30.0 or later) 23 | - [Language Support for Java by Red Hat](https://marketplace.visualstudio.com/items?itemName=redhat.java) 24 | 25 | ## Quick Start 26 | ![demo](https://raw.githubusercontent.com/jdneo/vscode-checkstyle/master/docs/gifs/demo.gif) 27 | 28 | ## Features 29 | 30 | ### Set Checkstyle Configuration File 31 | 32 | ![demo](https://raw.githubusercontent.com/jdneo/vscode-checkstyle/master/docs/gifs/setConfiguration.gif) 33 | 34 | - To set the configuration file, Just Right click the `.xml` file and select `Set the Checkstyle Configuration File`. 35 | 36 | - You can also trigger the command **Checkstyle: Set Checkstyle Configuration File** to choose the configuration file in the File Explorer. The extension will automatically detect and list the Checkstyle configuration files in your workspace. Besides that, you will also see the two built-in configurations: 37 | - **Google's Check** 38 | - **Sun's Check** 39 | 40 | ### Set Checkstyle Version 41 | 42 | ![demo](https://raw.githubusercontent.com/jdneo/vscode-checkstyle/master/docs/gifs/setVersion(lower).gif) 43 | 44 | - You can use the command `Checkstyle: Set the Checkstyle Version` to manually set the Checkstyle version according to your project preferences. The extension will automatically download the required jar files if they do not exist locally. 45 | 46 | > Note: If you don't know which `Checkstlye` version is used by `Maven Checkstyle Plugin`, check [this table](https://maven.apache.org/plugins/maven-checkstyle-plugin/history.html). 47 | 48 | ### Check the Style and Fix the Violations 49 | 50 | ![demo](https://raw.githubusercontent.com/jdneo/vscode-checkstyle/master/docs/gifs/liveLinting.gif) 51 | 52 | - When editing a Java file, the extension will check the file format and provide quick fixes if possible. You can click the ![bulb](https://raw.githubusercontent.com/jdneo/vscode-checkstyle/master/docs/imgs/btn_bulb.png) button in the editor to show the available quick fixes. 53 | 54 | 55 | 56 | ## Settings 57 | | Setting Name | Description | Default Value | 58 | |---|---|---| 59 | | `java.checkstyle.version` | Specify the Checkstyle Version. | `9.3` | 60 | | `java.checkstyle.configuration` | Specify the path of the Checkstyle configuration file. The path can either be a local file path or a URL. | `""` | 61 | | `java.checkstyle.properties` | Specify the customized properties used in the Checkstyle configuration. | `{}` | 62 | | `java.checkstyle.modules` | Specify the third-party modules used for Checkstyle. | `[]` | 63 | | `java.checkstyle.autocheck` | Specify if the extension will check the format automatically or not. | `true` | 64 | 65 | > Note: You can use the `${workspaceFolder}` to represent the path of the workspace folder of the file to be checked. For example: 66 | 67 | ```json 68 | "java.checkstyle.modules": [ 69 | "${workspaceFolder}/src/main/resources/sevntu-checks-1.35.0.jar" 70 | ] 71 | ``` 72 | or 73 | ```json 74 | "java.checkstyle.properties": { 75 | "basedir": "${workspaceFolder}" 76 | } 77 | ``` 78 | 79 | ## Release Notes 80 | 81 | Refer to [CHANGELOG.md](https://github.com/jdneo/vscode-checkstyle/blob/master/CHANGELOG.md) 82 | -------------------------------------------------------------------------------- /docs/gifs/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdneo/vscode-checkstyle/326c5b44877133088f7160660ac5a24007352c27/docs/gifs/demo.gif -------------------------------------------------------------------------------- /docs/gifs/liveLinting.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdneo/vscode-checkstyle/326c5b44877133088f7160660ac5a24007352c27/docs/gifs/liveLinting.gif -------------------------------------------------------------------------------- /docs/gifs/setConfiguration.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdneo/vscode-checkstyle/326c5b44877133088f7160660ac5a24007352c27/docs/gifs/setConfiguration.gif -------------------------------------------------------------------------------- /docs/gifs/setVersion(lower).gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdneo/vscode-checkstyle/326c5b44877133088f7160660ac5a24007352c27/docs/gifs/setVersion(lower).gif -------------------------------------------------------------------------------- /docs/imgs/btn_bulb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdneo/vscode-checkstyle/326c5b44877133088f7160660ac5a24007352c27/docs/imgs/btn_bulb.png -------------------------------------------------------------------------------- /docs/imgs/quick_fix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdneo/vscode-checkstyle/326c5b44877133088f7160660ac5a24007352c27/docs/imgs/quick_fix.png -------------------------------------------------------------------------------- /docs/imgs/set_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdneo/vscode-checkstyle/326c5b44877133088f7160660ac5a24007352c27/docs/imgs/set_config.png -------------------------------------------------------------------------------- /docs/imgs/set_version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdneo/vscode-checkstyle/326c5b44877133088f7160660ac5a24007352c27/docs/imgs/set_version.png -------------------------------------------------------------------------------- /javaConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": ["./jdtls.ext/com.shengchen.checkstyle.runner"], 3 | "targetPlatform": "./jdtls.ext/com.shengchen.checkstyle.target/target.target" 4 | } 5 | -------------------------------------------------------------------------------- /jdtls.ext/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdneo/vscode-checkstyle/326c5b44877133088f7160660ac5a24007352c27/jdtls.ext/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /jdtls.ext/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.checker/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.shengchen.checkstyle 8 | parent 9 | 1.4.2 10 | 11 | com.shengchen.checkstyle.checker 12 | jar 13 | ${base.name} :: Checker 14 | 15 | 16 | com.puppycrawl.tools 17 | checkstyle 18 | ${checkstyle-version} 19 | provided 20 | 21 | 22 | com.shengchen.checkstyle 23 | com.shengchen.checkstyle.runner 24 | ${project.parent.version} 25 | provided 26 | 27 | 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-checkstyle-plugin 33 | 34 | 35 | 36 | 37 | 17 38 | 17 39 | 40 | 41 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.checker/src/main/java/com/shengchen/checkstyle/checker/CheckerListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.checker; 19 | 20 | import com.puppycrawl.tools.checkstyle.api.AuditEvent; 21 | import com.puppycrawl.tools.checkstyle.api.AuditListener; 22 | import com.puppycrawl.tools.checkstyle.api.SeverityLevel; 23 | import com.shengchen.checkstyle.runner.api.CheckResult; 24 | 25 | import java.io.File; 26 | import java.lang.reflect.InvocationTargetException; 27 | import java.lang.reflect.Method; 28 | import java.util.ArrayList; 29 | import java.util.HashMap; 30 | import java.util.List; 31 | import java.util.Map; 32 | 33 | public class CheckerListener implements AuditListener { 34 | 35 | private Map> fileErrors = new HashMap<>(); 36 | 37 | @Override 38 | public void addError(AuditEvent error) { 39 | final SeverityLevel severity = error.getSeverityLevel(); 40 | if (severity.equals(SeverityLevel.IGNORE)) { 41 | return; 42 | } 43 | fileErrors.get(error.getFileName()).add(new CheckResult( 44 | error.getLine(), 45 | this.backCompatColumnIndex(error) + 1, 46 | error.getMessage(), 47 | severity.toString().toLowerCase(), 48 | error.getSourceName().substring(error.getSourceName().lastIndexOf('.') + 1))); 49 | } 50 | 51 | @Override 52 | public void fileStarted(AuditEvent event) { 53 | fileErrors.put(event.getFileName(), new ArrayList<>()); 54 | } 55 | 56 | @Override 57 | public void fileFinished(AuditEvent arg0) { 58 | return; 59 | } 60 | 61 | @Override 62 | public void auditStarted(AuditEvent arg0) { 63 | return; 64 | } 65 | 66 | @Override 67 | public void auditFinished(AuditEvent arg0) { 68 | return; 69 | } 70 | 71 | @Override 72 | public void addException(AuditEvent arg0, Throwable arg1) { 73 | return; 74 | } 75 | 76 | public Map> getResult(List filesToCheck) { 77 | final Map> result = new HashMap<>(); 78 | for (final File file: filesToCheck) { 79 | final String fileName = file.getAbsolutePath(); 80 | result.put(fileName, fileErrors.get(fileName)); 81 | } 82 | return result; 83 | } 84 | 85 | private int backCompatColumnIndex(final AuditEvent error) { 86 | // To keep backwards compatibility: 87 | try { 88 | // Try to use LocalizedMessage for Checkstyle versions <= v8.41.1 89 | final Method getLocalizedMessage = error.getClass().getMethod("getLocalizedMessage"); 90 | final Object localizedMessage = getLocalizedMessage.invoke(error); 91 | final Method getColumnCharIndex = localizedMessage.getClass().getMethod("getColumnCharIndex"); 92 | 93 | return (int) getColumnCharIndex.invoke(localizedMessage); 94 | } catch (NoSuchMethodException | 95 | SecurityException | 96 | IllegalAccessException | 97 | IllegalArgumentException | 98 | InvocationTargetException e1 99 | ) { 100 | // If LocalizedMessage does not exist, Checkstyle version >= v8.42 is used 101 | return error.getViolation().getColumnCharIndex(); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.checker/src/main/java/com/shengchen/checkstyle/checker/CheckerService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.checker; 19 | 20 | import com.puppycrawl.tools.checkstyle.Checker; 21 | import com.puppycrawl.tools.checkstyle.ConfigurationLoader; 22 | import com.puppycrawl.tools.checkstyle.Main; 23 | import com.puppycrawl.tools.checkstyle.PropertiesExpander; 24 | import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 25 | import com.shengchen.checkstyle.runner.api.CheckResult; 26 | import com.shengchen.checkstyle.runner.api.ICheckerService; 27 | 28 | import java.io.File; 29 | import java.io.IOException; 30 | import java.util.List; 31 | import java.util.Map; 32 | import java.util.Properties; 33 | 34 | public class CheckerService implements ICheckerService { 35 | 36 | private Checker checker = null; 37 | private CheckerListener listener = null; 38 | 39 | public void initialize() { 40 | checker = new Checker(); 41 | listener = new CheckerListener(); 42 | // reset the basedir if it is set so it won't get into the plugins way 43 | // of determining workspace resources from checkstyle reported file names, see 44 | // https://sourceforge.net/tracker/?func=detail&aid=2880044&group_id=80344&atid=559497 45 | checker.setBasedir(null); 46 | checker.setModuleClassLoader(Checker.class.getClassLoader()); 47 | checker.addListener(listener); 48 | } 49 | 50 | public void dispose() { 51 | if (checker != null) { 52 | if (listener != null) { 53 | checker.removeListener(listener); 54 | listener = null; 55 | } 56 | checker.destroy(); 57 | checker = null; 58 | } 59 | } 60 | 61 | @SuppressWarnings("unchecked") 62 | public void setConfiguration(Map config) throws IOException, CheckstyleException { 63 | final String configurationFsPath = (String) config.get("path"); 64 | final Map properties = (Map) config.get("properties"); 65 | final Properties checkstyleProperties = new Properties(); 66 | checkstyleProperties.putAll(properties); 67 | checker.configure(ConfigurationLoader.loadConfiguration( 68 | configurationFsPath, 69 | new PropertiesExpander(checkstyleProperties) 70 | )); 71 | } 72 | 73 | public String getVersion() throws Exception { 74 | return Main.class.getPackage().getImplementationVersion(); 75 | } 76 | 77 | public Map> checkCode(List filesToCheck, String charset) throws Exception { 78 | checker.setCharset(charset); 79 | checker.process(filesToCheck); 80 | return listener.getResult(filesToCheck); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.shengchen.checkstyle.runner 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.pde.ManifestBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.pde.SchemaBuilder 20 | 21 | 22 | 23 | 24 | org.eclipse.m2e.core.maven2Builder 25 | 26 | 27 | 28 | 29 | 30 | org.eclipse.m2e.core.maven2Nature 31 | org.eclipse.pde.PluginNature 32 | org.eclipse.jdt.core.javanature 33 | 34 | 35 | 36 | 1708997989680 37 | 38 | 30 39 | 40 | org.eclipse.core.resources.regexFilterMatcher 41 | node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Bundle-ManifestVersion: 2 3 | Bundle-Name: com.shengchen.checkstyle.runner 4 | Bundle-SymbolicName: com.shengchen.checkstyle.runner;singleton:=true 5 | Bundle-Version: 1.4.2 6 | Bundle-Activator: com.shengchen.checkstyle.runner.CheckstylePlugin 7 | Bundle-RequiredExecutionEnvironment: JavaSE-17 8 | Import-Package: org.eclipse.jdt.core, 9 | org.eclipse.jdt.launching, 10 | org.osgi.framework;version="1.3.0" 11 | Bundle-ActivationPolicy: lazy 12 | Require-Bundle: org.eclipse.jdt.core, 13 | org.eclipse.lsp4j, 14 | org.eclipse.jdt.launching, 15 | org.eclipse.jdt.ls.core, 16 | org.eclipse.core.runtime, 17 | org.eclipse.core.resources, 18 | org.eclipse.jdt.core.manipulation, 19 | org.eclipse.text 20 | Bundle-ClassPath: . 21 | Automatic-Module-Name: com.shengchen.checkstyle.runner 22 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/build.properties: -------------------------------------------------------------------------------- 1 | source.. = src/main/java 2 | output.. = target/classes 3 | bin.includes = META-INF/,\ 4 | .,\ 5 | plugin.xml 6 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.shengchen.checkstyle 8 | parent 9 | 1.4.2 10 | 11 | com.shengchen.checkstyle.runner 12 | eclipse-plugin 13 | ${base.name} :: Plugin 14 | 15 | 16 | 17 | org.eclipse.tycho 18 | tycho-maven-plugin 19 | ${tycho-version} 20 | true 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-checkstyle-plugin 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/FixableCheck.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix; 19 | 20 | public enum FixableCheck { 21 | // Blocks 22 | NEED_BRACE_CHECK("NeedBracesCheck"), AVOID_NESTED_BLOCKS_CHECK("AvoidNestedBlocksCheck"), 23 | 24 | // Coding 25 | FINAL_LOCAL_VARIABLE_CHECK("FinalLocalVariableCheck"), DEFAULT_COMES_LAST_CHECK("DefaultComesLastCheck"), 26 | EMPTY_STATEMENT_CHECK("EmptyStatementCheck"), MISSING_SWITCH_DEFAULT_CHECK("MissingSwitchDefaultCheck"), 27 | EXPLICIT_INITIALIZATION_CHECK("ExplicitInitializationCheck"), REQUIRE_THIS_CHECK("RequireThisCheck"), 28 | SIMPLIFY_BOOLEAN_RETURN_CHECK("SimplifyBooleanReturnCheck"), STRING_LITERAL_EQUALITY("StringLiteralEqualityCheck"), 29 | MULTIPLE_VARIABLE_DECLARATIONS_CHECK("MultipleVariableDeclarationsCheck"), 30 | 31 | // Design 32 | DESIGN_FOR_EXTENSION_CHECK("DesignForExtensionCheck"), FINAL_CLASS_CHECK("FinalClassCheck"), 33 | 34 | // Modifier 35 | MODIFIER_ORDER_CHECK("ModifierOrderCheck"), REDUNDANT_MODIFIER_CHECK("RedundantModifierCheck"), 36 | 37 | // Misc 38 | FINAL_PARAMETERS_CHECK("FinalParametersCheck"), UNCOMMENTED_MAIN_CHECK("UncommentedMainCheck"), 39 | UPPER_ELL_CHECK("UpperEllCheck"), ARRAY_TYPE_STYLE_CHECK("ArrayTypeStyleCheck"); 40 | 41 | private final String check; 42 | 43 | FixableCheck(final String check) { 44 | this.check = check; 45 | } 46 | 47 | /* 48 | * (non-Javadoc) 49 | * 50 | * @see java.lang.Enum#toString() 51 | */ 52 | @Override 53 | public String toString() { 54 | return check; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/QuickFixService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix; 19 | 20 | import com.shengchen.checkstyle.quickfix.blocks.AvoidNestedBlocksQuickFix; 21 | import com.shengchen.checkstyle.quickfix.blocks.NeedBracesQuickFix; 22 | import com.shengchen.checkstyle.quickfix.coding.DefaultComesLastQuickFix; 23 | import com.shengchen.checkstyle.quickfix.coding.EmptyStatementQuickFix; 24 | import com.shengchen.checkstyle.quickfix.coding.ExplicitInitializationQuickFix; 25 | import com.shengchen.checkstyle.quickfix.coding.FinalLocalVariableQuickFix; 26 | import com.shengchen.checkstyle.quickfix.coding.MissingSwitchDefaultQuickFix; 27 | import com.shengchen.checkstyle.quickfix.coding.MultipleVariableDeclarationsQuickFix; 28 | import com.shengchen.checkstyle.quickfix.coding.RequireThisQuickFix; 29 | import com.shengchen.checkstyle.quickfix.coding.SimplifyBooleanReturnQuickFix; 30 | import com.shengchen.checkstyle.quickfix.coding.StringLiteralEqualityQuickFix; 31 | import com.shengchen.checkstyle.quickfix.design.DesignForExtensionQuickFix; 32 | import com.shengchen.checkstyle.quickfix.design.FinalClassQuickFix; 33 | import com.shengchen.checkstyle.quickfix.misc.ArrayTypeStyleQuickFix; 34 | import com.shengchen.checkstyle.quickfix.misc.FinalParametersQuickFix; 35 | import com.shengchen.checkstyle.quickfix.misc.UncommentedMainQuickFix; 36 | import com.shengchen.checkstyle.quickfix.misc.UpperEllQuickFix; 37 | import com.shengchen.checkstyle.quickfix.modifier.ModifierOrderQuickFix; 38 | import com.shengchen.checkstyle.quickfix.modifier.RedundantModifierQuickFix; 39 | import com.shengchen.checkstyle.quickfix.utils.EditUtils; 40 | import com.shengchen.checkstyle.runner.api.IQuickFixService; 41 | 42 | import org.eclipse.jdt.core.ICompilationUnit; 43 | import org.eclipse.jdt.core.JavaModelException; 44 | import org.eclipse.jdt.core.dom.ASTParser; 45 | import org.eclipse.jdt.core.dom.CompilationUnit; 46 | import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; 47 | import org.eclipse.jdt.ls.core.internal.JDTUtils; 48 | import org.eclipse.jface.text.BadLocationException; 49 | import org.eclipse.jface.text.Document; 50 | import org.eclipse.jface.text.IRegion; 51 | import org.eclipse.lsp4j.WorkspaceEdit; 52 | import org.eclipse.text.edits.TextEdit; 53 | 54 | import java.util.HashMap; 55 | import java.util.List; 56 | import java.util.Map; 57 | 58 | public class QuickFixService implements IQuickFixService { 59 | 60 | private final Map quickFixMap; 61 | 62 | public QuickFixService() { 63 | quickFixMap = new HashMap<>(); 64 | quickFixMap.put(FixableCheck.FINAL_LOCAL_VARIABLE_CHECK.toString(), new FinalLocalVariableQuickFix()); 65 | quickFixMap.put(FixableCheck.MODIFIER_ORDER_CHECK.toString(), new ModifierOrderQuickFix()); 66 | quickFixMap.put(FixableCheck.REDUNDANT_MODIFIER_CHECK.toString(), new RedundantModifierQuickFix()); 67 | quickFixMap.put(FixableCheck.NEED_BRACE_CHECK.toString(), new NeedBracesQuickFix()); 68 | quickFixMap.put(FixableCheck.AVOID_NESTED_BLOCKS_CHECK.toString(), new AvoidNestedBlocksQuickFix()); 69 | quickFixMap.put(FixableCheck.DESIGN_FOR_EXTENSION_CHECK.toString(), new DesignForExtensionQuickFix()); 70 | quickFixMap.put(FixableCheck.FINAL_CLASS_CHECK.toString(), new FinalClassQuickFix()); 71 | quickFixMap.put(FixableCheck.DEFAULT_COMES_LAST_CHECK.toString(), new DefaultComesLastQuickFix()); 72 | quickFixMap.put(FixableCheck.EMPTY_STATEMENT_CHECK.toString(), new EmptyStatementQuickFix()); 73 | quickFixMap.put(FixableCheck.MISSING_SWITCH_DEFAULT_CHECK.toString(), new MissingSwitchDefaultQuickFix()); 74 | quickFixMap.put(FixableCheck.EXPLICIT_INITIALIZATION_CHECK.toString(), new ExplicitInitializationQuickFix()); 75 | quickFixMap.put(FixableCheck.REQUIRE_THIS_CHECK.toString(), new RequireThisQuickFix()); 76 | quickFixMap.put(FixableCheck.FINAL_PARAMETERS_CHECK.toString(), new FinalParametersQuickFix()); 77 | quickFixMap.put(FixableCheck.UNCOMMENTED_MAIN_CHECK.toString(), new UncommentedMainQuickFix()); 78 | quickFixMap.put(FixableCheck.UPPER_ELL_CHECK.toString(), new UpperEllQuickFix()); 79 | quickFixMap.put(FixableCheck.ARRAY_TYPE_STYLE_CHECK.toString(), new ArrayTypeStyleQuickFix()); 80 | quickFixMap.put(FixableCheck.SIMPLIFY_BOOLEAN_RETURN_CHECK.toString(), new SimplifyBooleanReturnQuickFix()); 81 | quickFixMap.put(FixableCheck.STRING_LITERAL_EQUALITY.toString(), new StringLiteralEqualityQuickFix()); 82 | quickFixMap.put(FixableCheck.MULTIPLE_VARIABLE_DECLARATIONS_CHECK.toString(), 83 | new MultipleVariableDeclarationsQuickFix()); 84 | } 85 | 86 | public BaseQuickFix getQuickFix(String sourceName) { 87 | return quickFixMap.get(sourceName); 88 | } 89 | 90 | public WorkspaceEdit quickFix( 91 | String fileToCheckUri, 92 | List offsets, 93 | List sourceNames 94 | ) throws JavaModelException, IllegalArgumentException, BadLocationException { 95 | final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(fileToCheckUri); 96 | final Document document = new Document(unit.getSource()); 97 | final ASTParser astParser = ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL); 98 | astParser.setKind(ASTParser.K_COMPILATION_UNIT); 99 | astParser.setSource(unit); 100 | final CompilationUnit astRoot = (CompilationUnit) astParser.createAST(null); 101 | astRoot.recordModifications(); 102 | 103 | for (int i = 0; i < offsets.size(); i++) { 104 | final int offset = offsets.get(i).intValue(); 105 | final BaseQuickFix quickFix = getQuickFix(sourceNames.get(i)); 106 | if (quickFix != null) { 107 | final IRegion lineInfo = document.getLineInformationOfOffset(offset); 108 | astRoot.accept(quickFix.getCorrectingASTVisitor(lineInfo, offset)); 109 | } 110 | } 111 | 112 | final TextEdit edit = astRoot.rewrite(document, null); 113 | return EditUtils.convertToWorkspaceEdit(unit, edit); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/blocks/AvoidNestedBlocksQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.blocks; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTNode; 23 | import org.eclipse.jdt.core.dom.ASTVisitor; 24 | import org.eclipse.jdt.core.dom.Block; 25 | import org.eclipse.jdt.core.dom.SwitchStatement; 26 | import org.eclipse.jface.text.IRegion; 27 | 28 | import java.util.List; 29 | 30 | public class AvoidNestedBlocksQuickFix extends BaseQuickFix { 31 | 32 | @Override 33 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 34 | return new ASTVisitor() { 35 | 36 | @SuppressWarnings("unchecked") 37 | @Override 38 | public boolean visit(Block node) { 39 | 40 | if (containsPosition(lineInfo, node.getStartPosition())) { 41 | 42 | if (node.getParent() instanceof Block) { 43 | 44 | final List statements = ((Block) node.getParent()).statements(); 45 | final int index = statements.indexOf(node); 46 | 47 | statements.remove(node); 48 | statements.addAll(index, ASTNode.copySubtrees(node.getAST(), node.statements())); 49 | 50 | } else if (node.getParent() instanceof SwitchStatement) { 51 | 52 | final List statements = ((SwitchStatement) node.getParent()).statements(); 53 | final int index = statements.indexOf(node); 54 | 55 | statements.remove(node); 56 | statements.addAll(index, ASTNode.copySubtrees(node.getAST(), node.statements())); 57 | } 58 | } 59 | return true; 60 | } 61 | }; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/blocks/NeedBracesQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.blocks; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.AST; 23 | import org.eclipse.jdt.core.dom.ASTNode; 24 | import org.eclipse.jdt.core.dom.ASTVisitor; 25 | import org.eclipse.jdt.core.dom.Block; 26 | import org.eclipse.jdt.core.dom.DoStatement; 27 | import org.eclipse.jdt.core.dom.ForStatement; 28 | import org.eclipse.jdt.core.dom.IfStatement; 29 | import org.eclipse.jdt.core.dom.Statement; 30 | import org.eclipse.jdt.core.dom.WhileStatement; 31 | import org.eclipse.jface.text.IRegion; 32 | 33 | public class NeedBracesQuickFix extends BaseQuickFix { 34 | 35 | @Override 36 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 37 | return new ASTVisitor() { 38 | @Override 39 | public boolean visit(IfStatement node) { 40 | 41 | final int nodePos = node.getStartPosition(); 42 | final int nodeEnd = nodePos + node.getLength(); 43 | if ((nodePos >= lineInfo.getOffset() && nodePos <= (lineInfo.getOffset() + lineInfo.getLength())) || 44 | (nodePos <= lineInfo.getOffset() && nodeEnd >= lineInfo.getOffset() + lineInfo.getLength())) { 45 | bracifyIfStatement(node); 46 | } 47 | 48 | return true; 49 | } 50 | 51 | // TODO: recursively block the statement like what we did in if-block 52 | @Override 53 | public boolean visit(ForStatement node) { 54 | if (containsPosition(lineInfo, node.getStartPosition())) { 55 | final Block block = createBracifiedCopy(node.getAST(), node.getBody()); 56 | node.setBody(block); 57 | } 58 | 59 | return true; 60 | } 61 | 62 | @Override 63 | public boolean visit(DoStatement node) { 64 | if (containsPosition(lineInfo, node.getStartPosition())) { 65 | final Block block = createBracifiedCopy(node.getAST(), node.getBody()); 66 | node.setBody(block); 67 | } 68 | 69 | return true; 70 | } 71 | 72 | @Override 73 | public boolean visit(WhileStatement node) { 74 | if (containsPosition(lineInfo, node.getStartPosition())) { 75 | final Block block = createBracifiedCopy(node.getAST(), node.getBody()); 76 | node.setBody(block); 77 | } 78 | 79 | return true; 80 | } 81 | 82 | /** 83 | * Helper method to recursively bracify a if-statement. 84 | * 85 | * @param ifStatement the if statement 86 | */ 87 | private void bracifyIfStatement(IfStatement ifStatement) { 88 | 89 | // change the then statement to a block if necessary 90 | if (!(ifStatement.getThenStatement() instanceof Block)) { 91 | if (ifStatement.getThenStatement() instanceof IfStatement) { 92 | bracifyIfStatement((IfStatement) ifStatement.getThenStatement()); 93 | } 94 | final Block block = createBracifiedCopy(ifStatement.getAST(), ifStatement.getThenStatement()); 95 | ifStatement.setThenStatement(block); 96 | } 97 | 98 | // check the else statement if it is a block 99 | final Statement elseStatement = ifStatement.getElseStatement(); 100 | if (elseStatement != null && !(elseStatement instanceof Block)) { 101 | 102 | // in case the else statement is an further if statement 103 | // (else if) 104 | // do the recursion 105 | if (elseStatement instanceof IfStatement) { 106 | bracifyIfStatement((IfStatement) elseStatement); 107 | } else { 108 | // change the else statement to a block 109 | // Block block = ifStatement.getAST().newBlock(); 110 | // block.statements().add(ASTNode.copySubtree(block.getAST(), 111 | // elseStatement)); 112 | final Block block = createBracifiedCopy(ifStatement.getAST(), ifStatement.getElseStatement()); 113 | ifStatement.setElseStatement(block); 114 | } 115 | } 116 | } 117 | 118 | @SuppressWarnings("unchecked") 119 | private Block createBracifiedCopy(AST ast, Statement body) { 120 | final Block block = ast.newBlock(); 121 | block.statements().add(ASTNode.copySubtree(block.getAST(), body)); 122 | return block; 123 | } 124 | }; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/coding/DefaultComesLastQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.coding; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTNode; 23 | import org.eclipse.jdt.core.dom.ASTVisitor; 24 | import org.eclipse.jdt.core.dom.SwitchCase; 25 | import org.eclipse.jdt.core.dom.SwitchStatement; 26 | import org.eclipse.jface.text.IRegion; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | public class DefaultComesLastQuickFix extends BaseQuickFix { 32 | 33 | @Override 34 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 35 | return new ASTVisitor() { 36 | 37 | @SuppressWarnings("unchecked") 38 | @Override 39 | public boolean visit(SwitchCase node) { 40 | 41 | if (containsPosition(lineInfo, node.getStartPosition())) { 42 | 43 | if (node.isDefault() && !isLastSwitchCase(node)) { 44 | final SwitchStatement switchStatement = (SwitchStatement) node.getParent(); 45 | 46 | final List defaultCaseStatements = new ArrayList<>(); 47 | defaultCaseStatements.add(node); 48 | 49 | // collect all statements belonging to the default case 50 | final int defaultStatementIndex = switchStatement.statements().indexOf(node); 51 | for (int i = defaultStatementIndex + 1; i < switchStatement.statements().size(); i++) { 52 | final ASTNode tmpNode = (ASTNode) switchStatement.statements().get(i); 53 | 54 | if (!(tmpNode instanceof SwitchCase)) { 55 | defaultCaseStatements.add(tmpNode); 56 | } else { 57 | break; 58 | } 59 | } 60 | 61 | // move the statements to the end of the statement list 62 | switchStatement.statements().removeAll(defaultCaseStatements); 63 | switchStatement.statements().addAll(defaultCaseStatements); 64 | } 65 | } 66 | return true; 67 | } 68 | 69 | private boolean isLastSwitchCase(SwitchCase switchCase) { 70 | 71 | final SwitchStatement switchStatement = (SwitchStatement) switchCase.getParent(); 72 | 73 | // collect all statements belonging to the default case 74 | final int defaultStatementIndex = switchStatement.statements().indexOf(switchCase); 75 | for (int i = defaultStatementIndex + 1; i < switchStatement.statements().size(); i++) { 76 | final ASTNode tmpNode = (ASTNode) switchStatement.statements().get(i); 77 | 78 | if (tmpNode instanceof SwitchCase) { 79 | return false; 80 | } 81 | } 82 | return true; 83 | } 84 | }; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/coding/EmptyStatementQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.coding; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTVisitor; 23 | import org.eclipse.jdt.core.dom.ChildPropertyDescriptor; 24 | import org.eclipse.jdt.core.dom.EmptyStatement; 25 | import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor; 26 | import org.eclipse.jface.text.IRegion; 27 | 28 | public class EmptyStatementQuickFix extends BaseQuickFix { 29 | 30 | @Override 31 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 32 | return new ASTVisitor() { 33 | @Override 34 | public boolean visit(EmptyStatement node) { 35 | if (containsPosition(lineInfo, node.getStartPosition())) { 36 | 37 | // early exit if the statement is mandatory, e.g. only 38 | // statement in a for-statement without block 39 | final StructuralPropertyDescriptor p = node.getLocationInParent(); 40 | if (p.isChildProperty() && ((ChildPropertyDescriptor) p).isMandatory()) { 41 | return false; 42 | } 43 | 44 | node.delete(); 45 | } 46 | return false; 47 | } 48 | }; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/coding/ExplicitInitializationQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.coding; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTVisitor; 23 | import org.eclipse.jdt.core.dom.VariableDeclarationFragment; 24 | import org.eclipse.jface.text.IRegion; 25 | 26 | public class ExplicitInitializationQuickFix extends BaseQuickFix { 27 | 28 | @Override 29 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 30 | return new ASTVisitor() { 31 | 32 | @Override 33 | public boolean visit(final VariableDeclarationFragment node) { 34 | if (containsPosition(node, markerStartOffset)) { 35 | node.getInitializer().delete(); 36 | } 37 | return false; 38 | } 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/coding/FinalLocalVariableQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.coding; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTVisitor; 23 | import org.eclipse.jdt.core.dom.Modifier; 24 | import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; 25 | import org.eclipse.jdt.core.dom.SingleVariableDeclaration; 26 | import org.eclipse.jdt.core.dom.VariableDeclarationStatement; 27 | import org.eclipse.jface.text.IRegion; 28 | 29 | public class FinalLocalVariableQuickFix extends BaseQuickFix { 30 | @Override 31 | @SuppressWarnings("unchecked") 32 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 33 | return new ASTVisitor() { 34 | 35 | @Override 36 | public boolean visit(SingleVariableDeclaration node) { 37 | if (containsPosition(node, markerStartOffset) && !Modifier.isFinal(node.getModifiers())) { 38 | final Modifier finalModifier = node.getAST().newModifier(ModifierKeyword.FINAL_KEYWORD); 39 | node.modifiers().add(finalModifier); 40 | } 41 | return true; 42 | } 43 | 44 | @Override 45 | public boolean visit(VariableDeclarationStatement node) { 46 | if (containsPosition(node, markerStartOffset) && !Modifier.isFinal(node.getModifiers())) { 47 | final Modifier finalModifier = node.getAST().newModifier(ModifierKeyword.FINAL_KEYWORD); 48 | node.modifiers().add(finalModifier); 49 | } 50 | return true; 51 | } 52 | }; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/coding/MissingSwitchDefaultQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.coding; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTVisitor; 23 | import org.eclipse.jdt.core.dom.SwitchCase; 24 | import org.eclipse.jdt.core.dom.SwitchStatement; 25 | import org.eclipse.jface.text.IRegion; 26 | 27 | public class MissingSwitchDefaultQuickFix extends BaseQuickFix { 28 | 29 | @Override 30 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 31 | return new ASTVisitor() { 32 | 33 | @SuppressWarnings("unchecked") 34 | @Override 35 | public boolean visit(SwitchStatement node) { 36 | if (containsPosition(lineInfo, node.getStartPosition())) { 37 | final SwitchCase defNode = node.getAST().newSwitchCase(); 38 | defNode.setExpression(null); 39 | node.statements().add(defNode); 40 | node.statements().add(node.getAST().newBreakStatement()); 41 | } 42 | return true; // also visit children 43 | } 44 | }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/coding/MultipleVariableDeclarationsQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.coding; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTNode; 23 | import org.eclipse.jdt.core.dom.ASTVisitor; 24 | import org.eclipse.jdt.core.dom.FieldDeclaration; 25 | import org.eclipse.jdt.core.dom.VariableDeclarationFragment; 26 | import org.eclipse.jdt.core.dom.VariableDeclarationStatement; 27 | import org.eclipse.jface.text.IRegion; 28 | 29 | import java.util.ArrayList; 30 | import java.util.Collection; 31 | import java.util.List; 32 | 33 | public class MultipleVariableDeclarationsQuickFix extends BaseQuickFix { 34 | 35 | @Override 36 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 37 | return new ASTVisitor() { 38 | 39 | @SuppressWarnings("unchecked") 40 | @Override 41 | public boolean visit(FieldDeclaration node) { 42 | if (containsPosition(node, markerStartOffset) && node.fragments().size() > 1) { 43 | final Collection replacements = new ArrayList<>(); 44 | for (final VariableDeclarationFragment fragment : 45 | (List) node.fragments()) { 46 | final FieldDeclaration newFieldDeclaration = node.getAST().newFieldDeclaration(copy(fragment)); 47 | newFieldDeclaration.setType(copy(node.getType())); 48 | newFieldDeclaration.modifiers().addAll(copy(node.modifiers())); 49 | if (replacements.isEmpty() && node.getJavadoc() != null) { 50 | newFieldDeclaration.setJavadoc(copy(node.getJavadoc())); 51 | } 52 | 53 | replacements.add(newFieldDeclaration); 54 | } 55 | 56 | replace(node, replacements); 57 | } 58 | return true; 59 | } 60 | 61 | @SuppressWarnings("unchecked") 62 | @Override 63 | public boolean visit(VariableDeclarationStatement node) { 64 | if (containsPosition(node, markerStartOffset) && node.fragments().size() > 1) { 65 | final Collection newVariableDeclarations = new ArrayList<>(); 66 | final List fragments = 67 | (List) node.fragments(); 68 | 69 | /* We keep the existing statement, as statements are associated with comments */ 70 | for (int i = 1; i < fragments.size(); i++) { 71 | final VariableDeclarationFragment fragment = fragments.get(i); 72 | final VariableDeclarationStatement newVariableDeclarationStatement = 73 | node.getAST().newVariableDeclarationStatement(copy(fragment)); 74 | newVariableDeclarationStatement.setType(copy(node.getType())); 75 | newVariableDeclarationStatement.modifiers().addAll(copy(node.modifiers())); 76 | 77 | newVariableDeclarations.add(newVariableDeclarationStatement); 78 | } 79 | 80 | /* Remove the additional fragments that we've duplicated */ 81 | while (fragments.size() > 1) { 82 | fragments.remove(1); 83 | } 84 | 85 | append(node, newVariableDeclarations); 86 | } 87 | return true; 88 | } 89 | }; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/coding/RequireThisQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.coding; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.AST; 23 | import org.eclipse.jdt.core.dom.ASTNode; 24 | import org.eclipse.jdt.core.dom.ASTVisitor; 25 | import org.eclipse.jdt.core.dom.Expression; 26 | import org.eclipse.jdt.core.dom.FieldAccess; 27 | import org.eclipse.jdt.core.dom.FieldDeclaration; 28 | import org.eclipse.jdt.core.dom.MethodDeclaration; 29 | import org.eclipse.jdt.core.dom.MethodInvocation; 30 | import org.eclipse.jdt.core.dom.SimpleName; 31 | import org.eclipse.jdt.core.dom.ThisExpression; 32 | import org.eclipse.jdt.core.dom.TypeDeclaration; 33 | import org.eclipse.jdt.core.dom.VariableDeclarationFragment; 34 | import org.eclipse.jface.text.IRegion; 35 | 36 | import java.util.List; 37 | 38 | public class RequireThisQuickFix extends BaseQuickFix { 39 | 40 | @Override 41 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 42 | return new ASTVisitor() { 43 | 44 | @Override 45 | public boolean visit(final SimpleName node) { 46 | if (containsPosition(node, markerStartOffset)) { 47 | replace(node, findFieldReplacement(node, node, 0)); 48 | } 49 | return false; 50 | } 51 | 52 | @Override 53 | public boolean visit(final MethodInvocation node) { 54 | if (containsPosition(node, markerStartOffset)) { 55 | replace(node, findMethodReplacement(node.getName(), node, node, 0)); 56 | } 57 | return false; 58 | } 59 | 60 | private Expression findFieldReplacement(final SimpleName name, final ASTNode node, int typeLevel) { 61 | 62 | int level = typeLevel; 63 | 64 | final ASTNode parent = node.getParent(); 65 | if (parent instanceof TypeDeclaration) { 66 | level++; 67 | final TypeDeclaration type = (TypeDeclaration) parent; 68 | for (final FieldDeclaration fieldDeclaration : type.getFields()) { 69 | @SuppressWarnings("unchecked") 70 | final List fragments = fieldDeclaration.fragments(); 71 | for (final VariableDeclarationFragment fragment : fragments) { 72 | if (name.getFullyQualifiedName().equals(fragment.getName().getFullyQualifiedName())) { 73 | return createFieldAccessReplacement(level == 1 ? null : type, name); 74 | } 75 | } 76 | } 77 | } 78 | return findFieldReplacement(name, parent, level); 79 | } 80 | 81 | private FieldAccess createFieldAccessReplacement(final TypeDeclaration type, final SimpleName name) { 82 | final AST ast = name.getAST(); 83 | final FieldAccess fieldAccess = ast.newFieldAccess(); 84 | final ThisExpression thisExpr = ast.newThisExpression(); 85 | if (type != null) { 86 | thisExpr.setQualifier(copy(type.getName())); 87 | } 88 | fieldAccess.setExpression(thisExpr); 89 | fieldAccess.setName(copy(name)); 90 | return fieldAccess; 91 | } 92 | 93 | private Expression findMethodReplacement(final SimpleName name, ASTNode contextNode, 94 | final MethodInvocation node, int typeLevel) { 95 | 96 | int level = typeLevel; 97 | 98 | final ASTNode parent = contextNode.getParent(); 99 | if (parent instanceof TypeDeclaration) { 100 | level++; 101 | final TypeDeclaration type = (TypeDeclaration) parent; 102 | for (final MethodDeclaration methodDeclaration : type.getMethods()) { 103 | if (name.getFullyQualifiedName().equals(methodDeclaration.getName().getFullyQualifiedName())) { 104 | return createMethodInvocationReplacement(level == 1 ? null : type, node); 105 | } 106 | } 107 | } 108 | return findMethodReplacement(name, parent, node, level); 109 | } 110 | 111 | private Expression createMethodInvocationReplacement(final TypeDeclaration type, 112 | MethodInvocation origMethodInvocation) { 113 | final AST ast = origMethodInvocation.getAST(); 114 | final MethodInvocation methodInvocation = copy(origMethodInvocation); 115 | final ThisExpression thisExpr = ast.newThisExpression(); 116 | if (type != null) { 117 | thisExpr.setQualifier(copy(type.getName())); 118 | } 119 | methodInvocation.setExpression(thisExpr); 120 | return methodInvocation; 121 | } 122 | 123 | }; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/coding/SimplifyBooleanReturnQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.coding; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.AST; 23 | import org.eclipse.jdt.core.dom.ASTVisitor; 24 | import org.eclipse.jdt.core.dom.Block; 25 | import org.eclipse.jdt.core.dom.BooleanLiteral; 26 | import org.eclipse.jdt.core.dom.Expression; 27 | import org.eclipse.jdt.core.dom.FieldAccess; 28 | import org.eclipse.jdt.core.dom.IfStatement; 29 | import org.eclipse.jdt.core.dom.MethodInvocation; 30 | import org.eclipse.jdt.core.dom.ParenthesizedExpression; 31 | import org.eclipse.jdt.core.dom.PrefixExpression; 32 | import org.eclipse.jdt.core.dom.PrefixExpression.Operator; 33 | import org.eclipse.jdt.core.dom.QualifiedName; 34 | import org.eclipse.jdt.core.dom.ReturnStatement; 35 | import org.eclipse.jdt.core.dom.SimpleName; 36 | import org.eclipse.jdt.core.dom.Statement; 37 | import org.eclipse.jdt.core.dom.SuperFieldAccess; 38 | import org.eclipse.jdt.core.dom.SuperMethodInvocation; 39 | import org.eclipse.jdt.core.dom.ThisExpression; 40 | import org.eclipse.jface.text.IRegion; 41 | 42 | import java.util.Arrays; 43 | import java.util.Collection; 44 | import java.util.List; 45 | 46 | public class SimplifyBooleanReturnQuickFix extends BaseQuickFix { 47 | 48 | /** 49 | * If the condition is of one of these expression types, the parantheses are not 50 | * necessary when negated. I.e the replacement can be written as 51 | * !condition instead of !(condition). 52 | */ 53 | private static final Collection> OMIT_PARANETHESES_CLASSES = Arrays.asList( 54 | BooleanLiteral.class, FieldAccess.class, MethodInvocation.class, QualifiedName.class, SimpleName.class, 55 | ParenthesizedExpression.class, SuperFieldAccess.class, SuperMethodInvocation.class, ThisExpression.class); 56 | 57 | @Override 58 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 59 | 60 | return new ASTVisitor() { 61 | 62 | @Override 63 | public boolean visit(final IfStatement node) { 64 | if (containsPosition(node, markerStartOffset)) { 65 | 66 | final Boolean isThenStatementTrue = isReturnStatementTrue(node.getThenStatement()); 67 | 68 | if (isThenStatementTrue == null) { 69 | // the AST structure of the if statement is not as expected 70 | return true; 71 | } 72 | 73 | final Expression condition = removeNotFromCondition(node.getExpression()); 74 | final boolean isNotCondition = condition != node.getExpression(); 75 | 76 | final ReturnStatement replacement; 77 | if (isThenStatementTrue ^ isNotCondition) { 78 | // create replacement: return condition; 79 | replacement = node.getAST().newReturnStatement(); 80 | replacement.setExpression(copy(condition)); 81 | 82 | } else { 83 | // create replacement: return !(condition); 84 | final AST ast = node.getAST(); 85 | replacement = ast.newReturnStatement(); 86 | final PrefixExpression not = ast.newPrefixExpression(); 87 | not.setOperator(Operator.NOT); 88 | if (omitParantheses(condition)) { 89 | not.setOperand(copy(condition)); 90 | } else { 91 | final ParenthesizedExpression parentheses = ast.newParenthesizedExpression(); 92 | parentheses.setExpression(copy(condition)); 93 | not.setOperand(parentheses); 94 | } 95 | replacement.setExpression(not); 96 | } 97 | replace(node, replacement); 98 | 99 | } 100 | return true; 101 | } 102 | 103 | private Boolean isReturnStatementTrue(final Statement node) { 104 | if (node instanceof ReturnStatement) { 105 | final Expression expression = ((ReturnStatement) node).getExpression(); 106 | if (expression instanceof BooleanLiteral) { 107 | return ((BooleanLiteral) expression).booleanValue(); 108 | } 109 | } else if (node instanceof Block) { 110 | // the return statement might be wrapped in a block statement 111 | @SuppressWarnings("unchecked") 112 | final List statements = ((Block) node).statements(); 113 | if (statements.size() > 0) { 114 | return isReturnStatementTrue(statements.get(0)); 115 | } 116 | } 117 | return null; 118 | } 119 | 120 | private Expression removeNotFromCondition(final Expression condition) { 121 | if (condition instanceof PrefixExpression) { 122 | final PrefixExpression prefix = (PrefixExpression) condition; 123 | if (PrefixExpression.Operator.NOT.equals(prefix.getOperator())) { 124 | return prefix.getOperand(); 125 | } 126 | } 127 | return condition; 128 | } 129 | 130 | private boolean omitParantheses(final Expression condition) { 131 | return OMIT_PARANETHESES_CLASSES.contains(condition.getClass()); 132 | } 133 | 134 | }; 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/coding/StringLiteralEqualityQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.coding; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTNode; 23 | import org.eclipse.jdt.core.dom.ASTVisitor; 24 | import org.eclipse.jdt.core.dom.Expression; 25 | import org.eclipse.jdt.core.dom.InfixExpression; 26 | import org.eclipse.jdt.core.dom.MethodInvocation; 27 | import org.eclipse.jdt.core.dom.PrefixExpression; 28 | import org.eclipse.jdt.core.dom.StringLiteral; 29 | import org.eclipse.jface.text.IRegion; 30 | 31 | import java.lang.reflect.InvocationTargetException; 32 | import java.lang.reflect.Method; 33 | import java.util.List; 34 | 35 | public class StringLiteralEqualityQuickFix extends BaseQuickFix { 36 | 37 | @Override 38 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 39 | return new ASTVisitor() { 40 | 41 | @SuppressWarnings("unchecked") 42 | @Override 43 | public boolean visit(InfixExpression node) { 44 | 45 | if (containsPosition(lineInfo, node.getStartPosition())) { 46 | 47 | StringLiteral literal = null; 48 | Expression otherOperand = null; 49 | 50 | if (node.getLeftOperand() instanceof StringLiteral) { 51 | literal = (StringLiteral) node.getLeftOperand(); 52 | otherOperand = node.getRightOperand(); 53 | } else if (node.getRightOperand() instanceof StringLiteral) { 54 | literal = (StringLiteral) node.getRightOperand(); 55 | otherOperand = node.getLeftOperand(); 56 | } else { 57 | return true; 58 | } 59 | 60 | Expression replacementNode = null; 61 | 62 | final MethodInvocation equalsInvocation = node.getAST().newMethodInvocation(); 63 | equalsInvocation.setName(node.getAST().newSimpleName("equals")); //$NON-NLS-1$ 64 | equalsInvocation.setExpression((Expression) ASTNode.copySubtree(node.getAST(), literal)); 65 | equalsInvocation.arguments().add(ASTNode.copySubtree(node.getAST(), otherOperand)); 66 | 67 | // if the string was compared with != create a not 68 | // expression 69 | if (node.getOperator().equals(InfixExpression.Operator.NOT_EQUALS)) { 70 | final PrefixExpression prefixExpression = node.getAST().newPrefixExpression(); 71 | prefixExpression.setOperator(PrefixExpression.Operator.NOT); 72 | prefixExpression.setOperand(equalsInvocation); 73 | replacementNode = prefixExpression; 74 | } else { 75 | replacementNode = equalsInvocation; 76 | } 77 | 78 | replaceNode(node, replacementNode); 79 | } 80 | return true; 81 | } 82 | 83 | /** 84 | * Replaces the given node with the replacement node (using reflection since I 85 | * am not aware of a proper API to do this). 86 | * 87 | * @param node the node to replace 88 | * @param replacementNode the replacement 89 | */ 90 | private void replaceNode(ASTNode node, ASTNode replacementNode) { 91 | try { 92 | if (node.getLocationInParent().isChildProperty()) { 93 | 94 | final String property = node.getLocationInParent().getId(); 95 | 96 | final String capitalizedProperty = property.substring(0, 1).toUpperCase() + 97 | property.substring(1); 98 | final String setterMethodName = "set" + capitalizedProperty; 99 | 100 | Class testClass = node.getClass(); 101 | 102 | while (testClass != null) { 103 | 104 | try { 105 | final Method setterMethod = node.getParent().getClass().getMethod(setterMethodName, 106 | testClass); 107 | setterMethod.invoke(node.getParent(), replacementNode); 108 | break; 109 | } catch (NoSuchMethodException e) { 110 | testClass = testClass.getSuperclass(); 111 | } 112 | } 113 | 114 | } else if (node.getLocationInParent().isChildListProperty()) { 115 | final Method listMethod = node.getParent().getClass() 116 | .getMethod(node.getLocationInParent().getId(), (Class[]) null); 117 | @SuppressWarnings("unchecked") 118 | final List list = (List) listMethod.invoke(node.getParent(), (Object[]) null); 119 | list.set(list.indexOf(node), replacementNode); 120 | } 121 | } catch (InvocationTargetException e) { 122 | // TODO: log 123 | } catch (IllegalAccessException e) { 124 | // TODO: log 125 | } catch (NoSuchMethodException e) { 126 | // TODO: log 127 | } 128 | } 129 | }; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/design/DesignForExtensionQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.design; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | import com.shengchen.checkstyle.quickfix.modifier.ModifierOrderQuickFix; 22 | 23 | import org.eclipse.jdt.core.dom.ASTNode; 24 | import org.eclipse.jdt.core.dom.ASTVisitor; 25 | import org.eclipse.jdt.core.dom.MethodDeclaration; 26 | import org.eclipse.jdt.core.dom.Modifier; 27 | import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; 28 | import org.eclipse.jface.text.IRegion; 29 | 30 | import java.util.List; 31 | 32 | public class DesignForExtensionQuickFix extends BaseQuickFix { 33 | 34 | /** The length of the javadoc comment declaration. */ 35 | private static final int JAVADOC_COMMENT_LENGTH = 6; 36 | 37 | @Override 38 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 39 | return new ASTVisitor() { 40 | 41 | @SuppressWarnings("unchecked") 42 | @Override 43 | public boolean visit(MethodDeclaration node) { 44 | // recalculate start position because optional javadoc is mixed 45 | // into the original start position 46 | final int pos = node.getStartPosition() + 47 | (node.getJavadoc() != null ? node.getJavadoc().getLength() + JAVADOC_COMMENT_LENGTH : 0); 48 | if (containsPosition(lineInfo, pos)) { 49 | 50 | if (!Modifier.isFinal(node.getModifiers())) { 51 | 52 | final Modifier finalModifier = node.getAST().newModifier(ModifierKeyword.FINAL_KEYWORD); 53 | node.modifiers().add(finalModifier); 54 | 55 | // reorder modifiers into their correct order 56 | final List reorderedModifiers = ModifierOrderQuickFix 57 | .reorderModifiers(node.modifiers()); 58 | node.modifiers().clear(); 59 | node.modifiers().addAll(reorderedModifiers); 60 | } 61 | } 62 | return true; 63 | } 64 | }; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/design/FinalClassQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.design; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | import com.shengchen.checkstyle.quickfix.modifier.ModifierOrderQuickFix; 22 | 23 | import org.eclipse.jdt.core.dom.ASTVisitor; 24 | import org.eclipse.jdt.core.dom.Modifier; 25 | import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; 26 | import org.eclipse.jdt.core.dom.TypeDeclaration; 27 | import org.eclipse.jface.text.IRegion; 28 | 29 | import java.util.List; 30 | 31 | public class FinalClassQuickFix extends BaseQuickFix { 32 | 33 | /** The length of the javadoc comment declaration. */ 34 | private static final int JAVADOC_COMMENT_LENGTH = 6; 35 | 36 | @Override 37 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 38 | return new ASTVisitor() { 39 | 40 | @SuppressWarnings("unchecked") 41 | @Override 42 | public boolean visit(TypeDeclaration node) { 43 | // recalculate start position because optional javadoc is mixed 44 | // into the original start position 45 | final int pos = node.getStartPosition() + 46 | (node.getJavadoc() != null ? node.getJavadoc().getLength() + JAVADOC_COMMENT_LENGTH : 0); 47 | if (containsPosition(lineInfo, pos)) { 48 | 49 | if (!Modifier.isFinal(node.getModifiers())) { 50 | 51 | final Modifier finalModifier = node.getAST().newModifier(ModifierKeyword.FINAL_KEYWORD); 52 | node.modifiers().add(finalModifier); 53 | 54 | // reorder modifiers into their correct order 55 | final List reorderedModifiers = ModifierOrderQuickFix.reorderModifiers(node.modifiers()); 56 | node.modifiers().clear(); 57 | node.modifiers().addAll(reorderedModifiers); 58 | } 59 | } 60 | return true; 61 | } 62 | }; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/misc/FinalParametersQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.misc; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTVisitor; 23 | import org.eclipse.jdt.core.dom.Modifier; 24 | import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; 25 | import org.eclipse.jdt.core.dom.SingleVariableDeclaration; 26 | import org.eclipse.jface.text.IRegion; 27 | 28 | public class FinalParametersQuickFix extends BaseQuickFix { 29 | 30 | @Override 31 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 32 | return new ASTVisitor() { 33 | 34 | @SuppressWarnings("unchecked") 35 | @Override 36 | public boolean visit(SingleVariableDeclaration node) { 37 | if (containsPosition(node, markerStartOffset) && !Modifier.isFinal(node.getModifiers())) { 38 | if (!Modifier.isFinal(node.getModifiers())) { 39 | final Modifier finalModifier = node.getAST().newModifier(ModifierKeyword.FINAL_KEYWORD); 40 | node.modifiers().add(finalModifier); 41 | } 42 | } 43 | return true; 44 | } 45 | }; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/misc/UncommentedMainQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.misc; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTVisitor; 23 | import org.eclipse.jdt.core.dom.MethodDeclaration; 24 | import org.eclipse.jface.text.IRegion; 25 | 26 | public class UncommentedMainQuickFix extends BaseQuickFix { 27 | 28 | /** The length of the javadoc comment declaration. */ 29 | private static final int JAVADOC_COMMENT_LENGTH = 6; 30 | 31 | @Override 32 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 33 | return new ASTVisitor() { 34 | 35 | @Override 36 | public boolean visit(MethodDeclaration node) { 37 | // recalculate start position because optional javadoc is mixed 38 | // into the original start position 39 | final int pos = node.getStartPosition() + 40 | (node.getJavadoc() != null ? node.getJavadoc().getLength() + JAVADOC_COMMENT_LENGTH : 0); 41 | if (containsPosition(lineInfo, pos) && node.getName().getFullyQualifiedName().equals("main")) { 42 | node.delete(); 43 | } 44 | return true; 45 | } 46 | }; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/misc/UpperEllQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.misc; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTVisitor; 23 | import org.eclipse.jdt.core.dom.NumberLiteral; 24 | import org.eclipse.jface.text.IRegion; 25 | 26 | public class UpperEllQuickFix extends BaseQuickFix { 27 | 28 | @Override 29 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 30 | return new ASTVisitor() { 31 | 32 | @Override 33 | public boolean visit(NumberLiteral node) { 34 | if (containsPosition(node, markerStartOffset)) { 35 | 36 | String token = node.getToken(); 37 | if (token.endsWith("l")) { //$NON-NLS-1$ 38 | token = token.replace('l', 'L'); 39 | node.setToken(token); 40 | } 41 | } 42 | return true; 43 | } 44 | }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/modifier/ModifierOrderQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.modifier; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTNode; 23 | import org.eclipse.jdt.core.dom.ASTVisitor; 24 | import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; 25 | import org.eclipse.jdt.core.dom.BodyDeclaration; 26 | import org.eclipse.jdt.core.dom.FieldDeclaration; 27 | import org.eclipse.jdt.core.dom.MethodDeclaration; 28 | import org.eclipse.jdt.core.dom.Modifier; 29 | import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; 30 | import org.eclipse.jdt.core.dom.TypeDeclaration; 31 | import org.eclipse.jface.text.IRegion; 32 | 33 | import java.util.ArrayList; 34 | import java.util.Arrays; 35 | import java.util.Collections; 36 | import java.util.Comparator; 37 | import java.util.Iterator; 38 | import java.util.List; 39 | import java.util.stream.Collectors; 40 | 41 | public class ModifierOrderQuickFix extends BaseQuickFix { 42 | 43 | /** 44 | * List containing modifier keywords in the order proposed by Java Language 45 | * specification, sections 8.1.1, 8.3.1 and 8.4.3. 46 | */ 47 | private static final List MODIFIER_ORDER = Arrays.asList(new Object[] { ModifierKeyword.PUBLIC_KEYWORD, 48 | ModifierKeyword.PROTECTED_KEYWORD, ModifierKeyword.PRIVATE_KEYWORD, ModifierKeyword.ABSTRACT_KEYWORD, 49 | ModifierKeyword.STATIC_KEYWORD, ModifierKeyword.FINAL_KEYWORD, ModifierKeyword.TRANSIENT_KEYWORD, 50 | ModifierKeyword.VOLATILE_KEYWORD, ModifierKeyword.SYNCHRONIZED_KEYWORD, ModifierKeyword.NATIVE_KEYWORD, 51 | ModifierKeyword.STRICTFP_KEYWORD, ModifierKeyword.DEFAULT_KEYWORD }); 52 | 53 | public static List reorderModifiers(List modifiers) { 54 | 55 | final List copies = new ArrayList<>(); 56 | final Iterator it = modifiers.iterator(); 57 | while (it.hasNext()) { 58 | final ASTNode mod = it.next(); 59 | copies.add(ASTNode.copySubtree(mod.getAST(), mod)); 60 | } 61 | 62 | Collections.sort(copies, new Comparator() { 63 | 64 | @Override 65 | public int compare(ASTNode arg0, ASTNode arg1) { 66 | if (!(arg0 instanceof Modifier) || !(arg1 instanceof Modifier)) { 67 | return 0; 68 | } 69 | 70 | final Modifier m1 = (Modifier) arg0; 71 | final Modifier m2 = (Modifier) arg1; 72 | 73 | final int modifierIndex1 = MODIFIER_ORDER.indexOf(m1.getKeyword()); 74 | final int modifierIndex2 = MODIFIER_ORDER.indexOf(m2.getKeyword()); 75 | 76 | return new Integer(modifierIndex1).compareTo(new Integer(modifierIndex2)); 77 | } 78 | }); 79 | 80 | return copies; 81 | } 82 | 83 | @Override 84 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 85 | return new ASTVisitor() { 86 | 87 | @Override 88 | public boolean visit(TypeDeclaration node) { 89 | return visitBodyDecl(node); 90 | } 91 | 92 | @Override 93 | public boolean visit(MethodDeclaration node) { 94 | return visitBodyDecl(node); 95 | } 96 | 97 | @Override 98 | public boolean visit(FieldDeclaration node) { 99 | return visitBodyDecl(node); 100 | } 101 | 102 | @Override 103 | public boolean visit(AnnotationTypeMemberDeclaration node) { 104 | return visitBodyDecl(node); 105 | } 106 | 107 | @SuppressWarnings("unchecked") 108 | private boolean visitBodyDecl(BodyDeclaration node) { 109 | final List modifiers = (List) node.modifiers().stream() 110 | .filter(Modifier.class::isInstance).map(Modifier.class::cast).collect(Collectors.toList()); 111 | if (modifiers == null || modifiers.isEmpty()) { 112 | return true; 113 | } 114 | // find the range from first to last modifier. marker must be in between 115 | final int minPos = modifiers.stream().mapToInt(Modifier::getStartPosition).min().getAsInt(); 116 | final int maxPos = modifiers.stream().mapToInt(Modifier::getStartPosition).max().getAsInt(); 117 | 118 | if (minPos <= markerStartOffset && markerStartOffset <= maxPos) { 119 | final List reorderedModifiers = reorderModifiers(node.modifiers()); 120 | node.modifiers().clear(); 121 | node.modifiers().addAll(reorderedModifiers); 122 | } 123 | return true; 124 | } 125 | }; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/modifier/RedundantModifierQuickFix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.modifier; 19 | 20 | import com.shengchen.checkstyle.quickfix.BaseQuickFix; 21 | 22 | import org.eclipse.jdt.core.dom.ASTNode; 23 | import org.eclipse.jdt.core.dom.ASTVisitor; 24 | import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; 25 | import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; 26 | import org.eclipse.jdt.core.dom.FieldDeclaration; 27 | import org.eclipse.jdt.core.dom.MethodDeclaration; 28 | import org.eclipse.jdt.core.dom.Modifier; 29 | import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; 30 | import org.eclipse.jdt.core.dom.TypeDeclaration; 31 | import org.eclipse.jface.text.IRegion; 32 | 33 | import java.util.Arrays; 34 | import java.util.Collections; 35 | import java.util.Iterator; 36 | import java.util.List; 37 | 38 | public class RedundantModifierQuickFix extends BaseQuickFix { 39 | 40 | /** The length of the javadoc comment declaration. */ 41 | private static final int JAVADOC_COMMENT_LENGTH = 6; 42 | 43 | @Override 44 | public ASTVisitor getCorrectingASTVisitor(IRegion lineInfo, int markerStartOffset) { 45 | return new ASTVisitor() { 46 | 47 | @SuppressWarnings("unchecked") 48 | public boolean visit(TypeDeclaration node) { 49 | if (containsPosition(node, markerStartOffset)) { 50 | List redundantKeyWords = Collections.emptyList(); 51 | 52 | if (node.isInterface()) { 53 | redundantKeyWords = Arrays.asList(new ModifierKeyword[] { ModifierKeyword.ABSTRACT_KEYWORD, 54 | ModifierKeyword.STATIC_KEYWORD }); 55 | } 56 | 57 | deleteRedundantModifiers(node.modifiers(), redundantKeyWords); 58 | } 59 | return true; 60 | } 61 | 62 | @SuppressWarnings("unchecked") 63 | @Override 64 | public boolean visit(MethodDeclaration node) { 65 | 66 | if (containsPosition(node, markerStartOffset)) { 67 | List redundantKeyWords = Collections.emptyList(); 68 | 69 | if (node.getParent() instanceof TypeDeclaration) { 70 | final TypeDeclaration type = (TypeDeclaration) node.getParent(); 71 | if (type.isInterface()) { 72 | redundantKeyWords = Arrays.asList(new ModifierKeyword[] { ModifierKeyword.PUBLIC_KEYWORD, 73 | ModifierKeyword.ABSTRACT_KEYWORD, ModifierKeyword.FINAL_KEYWORD }); 74 | } else if (Modifier.isFinal(type.getModifiers())) { 75 | redundantKeyWords = Arrays.asList(new ModifierKeyword[] { ModifierKeyword.FINAL_KEYWORD }); 76 | } 77 | } 78 | 79 | deleteRedundantModifiers(node.modifiers(), redundantKeyWords); 80 | } 81 | return true; 82 | } 83 | 84 | @SuppressWarnings("unchecked") 85 | @Override 86 | public boolean visit(FieldDeclaration node) { 87 | // recalculate start position because optional javadoc is mixed 88 | // into the original start position 89 | final int pos = node.getStartPosition() + 90 | (node.getJavadoc() != null ? node.getJavadoc().getLength() + JAVADOC_COMMENT_LENGTH : 0); 91 | if (containsPosition(lineInfo, pos)) { 92 | List redundantKeyWords = Collections.emptyList(); 93 | 94 | if (node.getParent() instanceof TypeDeclaration) { 95 | final TypeDeclaration type = (TypeDeclaration) node.getParent(); 96 | if (type.isInterface()) { 97 | redundantKeyWords = Arrays.asList(new ModifierKeyword[] { ModifierKeyword.PUBLIC_KEYWORD, 98 | ModifierKeyword.ABSTRACT_KEYWORD, ModifierKeyword.FINAL_KEYWORD, 99 | ModifierKeyword.STATIC_KEYWORD }); 100 | } 101 | } else if (node.getParent() instanceof AnnotationTypeDeclaration) { 102 | 103 | redundantKeyWords = Arrays.asList(new ModifierKeyword[] { ModifierKeyword.PUBLIC_KEYWORD, 104 | ModifierKeyword.ABSTRACT_KEYWORD, ModifierKeyword.FINAL_KEYWORD, 105 | ModifierKeyword.STATIC_KEYWORD }); 106 | } 107 | 108 | deleteRedundantModifiers(node.modifiers(), redundantKeyWords); 109 | } 110 | return true; 111 | } 112 | 113 | @SuppressWarnings("unchecked") 114 | @Override 115 | public boolean visit(AnnotationTypeMemberDeclaration node) { 116 | 117 | // recalculate start position because optional javadoc is mixed 118 | // into the original start position 119 | final int pos = node.getStartPosition() + 120 | (node.getJavadoc() != null ? node.getJavadoc().getLength() + JAVADOC_COMMENT_LENGTH : 0); 121 | if (containsPosition(lineInfo, pos)) { 122 | 123 | if (node.getParent() instanceof AnnotationTypeDeclaration) { 124 | 125 | final List redundantKeyWords = Arrays.asList(new ModifierKeyword[] { 126 | ModifierKeyword.PUBLIC_KEYWORD, ModifierKeyword.ABSTRACT_KEYWORD, 127 | ModifierKeyword.FINAL_KEYWORD, ModifierKeyword.STATIC_KEYWORD }); 128 | 129 | deleteRedundantModifiers(node.modifiers(), redundantKeyWords); 130 | } 131 | 132 | } 133 | return true; 134 | } 135 | 136 | private void deleteRedundantModifiers(List modifiers, 137 | List redundantModifierKeywords) { 138 | 139 | final Iterator it = modifiers.iterator(); 140 | 141 | while (it.hasNext()) { 142 | final ASTNode node = it.next(); 143 | 144 | if (node instanceof Modifier) { 145 | final Modifier modifier = (Modifier) node; 146 | if (redundantModifierKeywords.contains(modifier.getKeyword())) { 147 | it.remove(); 148 | } 149 | } 150 | } 151 | } 152 | }; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/quickfix/utils/EditUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.quickfix.utils; 19 | 20 | import org.eclipse.jdt.core.ICompilationUnit; 21 | import org.eclipse.jdt.ls.core.internal.JDTUtils; 22 | import org.eclipse.jdt.ls.core.internal.TextEditConverter; 23 | import org.eclipse.lsp4j.WorkspaceEdit; 24 | import org.eclipse.text.edits.TextEdit; 25 | 26 | @SuppressWarnings("restriction") 27 | public class EditUtils { 28 | 29 | public static WorkspaceEdit convertToWorkspaceEdit(ICompilationUnit unit, TextEdit edit) { 30 | final WorkspaceEdit workspaceEdit = new WorkspaceEdit(); 31 | final TextEditConverter converter = new TextEditConverter(unit, edit); 32 | final String uri = JDTUtils.toURI(unit); 33 | workspaceEdit.getChanges().put(uri, converter.convert()); 34 | return workspaceEdit; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/runner/CheckstyleLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.runner; 19 | 20 | import com.shengchen.checkstyle.quickfix.QuickFixService; 21 | import com.shengchen.checkstyle.runner.api.ICheckerService; 22 | import com.shengchen.checkstyle.runner.api.IQuickFixService; 23 | 24 | import java.io.File; 25 | import java.lang.reflect.Constructor; 26 | import java.net.URL; 27 | import java.net.URLClassLoader; 28 | import java.nio.file.Paths; 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | 32 | public class CheckstyleLoader { 33 | 34 | static String checkerClass = "com.shengchen.checkstyle.checker.CheckerService"; 35 | 36 | URLClassLoader checkerClassLoader = null; 37 | 38 | public ICheckerService loadCheckerService(String checkstyleJarPath, List modulejarPaths) throws Exception { 39 | if (checkerClassLoader != null) { 40 | checkerClassLoader.close(); 41 | } 42 | final ArrayList jarUrls = new ArrayList<>(); 43 | jarUrls.add(Paths.get(getServerDir(), "com.shengchen.checkstyle.checker.jar").toUri().toURL()); 44 | jarUrls.add(Paths.get(checkstyleJarPath).toUri().toURL()); 45 | for (final String module: modulejarPaths) { 46 | jarUrls.add(Paths.get(module).toUri().toURL()); 47 | } 48 | checkerClassLoader = new URLClassLoader(jarUrls.toArray(new URL[0]), getClass().getClassLoader()); 49 | final Constructor constructor = checkerClassLoader.loadClass(checkerClass).getConstructor(); 50 | return (ICheckerService) constructor.newInstance(); 51 | } 52 | 53 | public IQuickFixService loadQuickFixService() { 54 | return new QuickFixService(); 55 | } 56 | 57 | private String getServerDir() throws Exception { 58 | final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getFile()); 59 | return jarFile.getParentFile().getCanonicalPath(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/runner/CheckstylePlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.runner; 19 | 20 | import org.osgi.framework.BundleActivator; 21 | import org.osgi.framework.BundleContext; 22 | 23 | public class CheckstylePlugin implements BundleActivator { 24 | 25 | public static final String PLUGIN_ID = "com.shengchen.checkstyle.runner"; 26 | public static BundleContext context = null; 27 | 28 | @Override 29 | public void start(BundleContext context) throws Exception { 30 | CheckstylePlugin.context = context; 31 | } 32 | 33 | @Override 34 | public void stop(BundleContext context) throws Exception { 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/runner/DelegateCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.runner; 19 | 20 | import com.shengchen.checkstyle.runner.api.CheckResult; 21 | import com.shengchen.checkstyle.runner.api.ICheckerService; 22 | import com.shengchen.checkstyle.runner.api.IQuickFixService; 23 | 24 | import org.eclipse.core.resources.IFile; 25 | import org.eclipse.core.runtime.IProgressMonitor; 26 | import org.eclipse.jdt.core.JavaModelException; 27 | import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler; 28 | import org.eclipse.jdt.ls.core.internal.JDTUtils; 29 | import org.eclipse.jface.text.BadLocationException; 30 | import org.eclipse.lsp4j.WorkspaceEdit; 31 | 32 | import java.io.File; 33 | import java.lang.reflect.Method; 34 | import java.util.Collections; 35 | import java.util.List; 36 | import java.util.Map; 37 | import java.util.stream.Collectors; 38 | 39 | @SuppressWarnings("restriction") 40 | public class DelegateCommandHandler implements IDelegateCommandHandler { 41 | 42 | private static final String CHECKSTYLE_PREFIX = "java.checkstyle.server."; 43 | 44 | private CheckstyleLoader checkstyleLoader = new CheckstyleLoader(); 45 | private ICheckerService checkerService = null; 46 | private IQuickFixService quickfixService = null; 47 | 48 | @Override 49 | public synchronized Object executeCommand( 50 | String commandId, 51 | List arguments, 52 | IProgressMonitor monitor 53 | ) throws Exception { 54 | if (commandId.startsWith(CHECKSTYLE_PREFIX)) { // Only handle commands with specific id prefix 55 | final String command = commandId.substring(CHECKSTYLE_PREFIX.length()); // Remove prefix as handler name 56 | for (final Method handler : this.getClass().getDeclaredMethods()) { 57 | if (handler.getName().equals(command)) { // Dispatch to CheckStyleRunner's corresponding handler 58 | return handler.invoke(this, arguments.toArray()); 59 | } 60 | } 61 | } 62 | return null; 63 | } 64 | 65 | @SuppressWarnings("unchecked") 66 | protected void setConfiguration(Map config) throws Throwable { 67 | final String jarStorage = (String) config.get("jarStorage"); 68 | final String version = (String) config.get("version"); 69 | final String jarPath = String.format("%s/checkstyle-%s-all.jar", jarStorage, version); 70 | final List modules = (List) config.get("modules"); 71 | if (checkerService != null) { 72 | checkerService.dispose(); 73 | } 74 | if (!version.equals(getVersion())) { // If not equal, load new version 75 | checkerService = checkstyleLoader.loadCheckerService(jarPath, modules); 76 | } 77 | try { 78 | checkerService.initialize(); 79 | checkerService.setConfiguration(config); 80 | } catch (Throwable throwable) { // Initialization faild 81 | checkerService.dispose(); // Unwind what's already initialized 82 | checkerService = null; // Remove checkerService 83 | throw throwable; // Resend the exception or error out 84 | } 85 | } 86 | 87 | protected String getVersion() throws Exception { 88 | if (checkerService != null) { 89 | return checkerService.getVersion(); 90 | } 91 | return null; 92 | } 93 | 94 | protected Map> checkCode(List filesToCheckUris) throws Exception { 95 | if (filesToCheckUris.isEmpty() || checkerService == null) { 96 | return Collections.emptyMap(); 97 | } 98 | final List filesToCheck = filesToCheckUris.stream().map(File::new).collect(Collectors.toList()); 99 | final IFile resource = JDTUtils.findFile(filesToCheck.get(0).toURI().toString()); 100 | return checkerService.checkCode(filesToCheck, resource != null ? resource.getCharset() : "utf8"); 101 | } 102 | 103 | protected WorkspaceEdit quickFix( 104 | String fileToCheckUri, 105 | List offsets, 106 | List sourceNames 107 | ) throws JavaModelException, IllegalArgumentException, BadLocationException { 108 | if (quickfixService == null) { 109 | quickfixService = checkstyleLoader.loadQuickFixService(); 110 | } 111 | return quickfixService.quickFix(fileToCheckUri, offsets, sourceNames); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/runner/api/CheckResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.runner.api; 19 | 20 | public class CheckResult { 21 | private int line; 22 | private int column; 23 | private String message; 24 | private String severity; 25 | private String sourceName; 26 | 27 | public CheckResult(int line, int column, String msg, String severity, String sourceName) { 28 | this.line = line; 29 | this.column = column; 30 | this.message = msg; 31 | this.severity = severity; 32 | this.sourceName = sourceName; 33 | } 34 | 35 | public int getLine() { 36 | return line; 37 | } 38 | 39 | public void setLine(int line) { 40 | this.line = line; 41 | } 42 | 43 | public int getColumn() { 44 | return column; 45 | } 46 | 47 | public void setColumn(int column) { 48 | this.column = column; 49 | } 50 | 51 | public String getMsg() { 52 | return message; 53 | } 54 | 55 | public void setMsg(String msg) { 56 | this.message = msg; 57 | } 58 | 59 | public String getSeverity() { 60 | return severity; 61 | } 62 | 63 | public void setSeverity(String severity) { 64 | this.severity = severity; 65 | } 66 | 67 | public String getSourceName() { 68 | return sourceName; 69 | } 70 | 71 | public void setSourceName(String sourceName) { 72 | this.sourceName = sourceName; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/runner/api/ICheckerService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.runner.api; 19 | 20 | import java.io.File; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | public interface ICheckerService { 25 | 26 | public void initialize() throws Exception; 27 | 28 | public void dispose() throws Exception; 29 | 30 | public void setConfiguration(Map config) throws Exception; 31 | 32 | public String getVersion() throws Exception; 33 | 34 | public Map> checkCode(List filesToCheck, String charset) throws Exception; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.runner/src/main/java/com/shengchen/checkstyle/runner/api/IQuickFixService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.shengchen.checkstyle.runner.api; 19 | 20 | import org.eclipse.jdt.core.JavaModelException; 21 | import org.eclipse.jface.text.BadLocationException; 22 | import org.eclipse.lsp4j.WorkspaceEdit; 23 | 24 | import java.util.List; 25 | 26 | public interface IQuickFixService { 27 | 28 | public WorkspaceEdit quickFix( 29 | String fileToCheckUri, 30 | List offsets, 31 | List sourceNames 32 | ) throws JavaModelException, IllegalArgumentException, BadLocationException; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.target/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.shengchen.checkstyle 8 | parent 9 | 1.4.2 10 | 11 | com.shengchen.checkstyle.target 12 | ${base.name} :: Target Platform 13 | eclipse-target-definition 14 | 15 | -------------------------------------------------------------------------------- /jdtls.ext/com.shengchen.checkstyle.target/target.target: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /jdtls.ext/config/java.header: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) jdneo 3 | 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * any later version. 8 | 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | -------------------------------------------------------------------------------- /jdtls.ext/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | if [ "$MVNW_VERBOSE" = true ]; then 205 | echo $MAVEN_PROJECTBASEDIR 206 | fi 207 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 208 | 209 | # For Cygwin, switch paths to Windows format before running java 210 | if $cygwin; then 211 | [ -n "$M2_HOME" ] && 212 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 213 | [ -n "$JAVA_HOME" ] && 214 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 215 | [ -n "$CLASSPATH" ] && 216 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 217 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 218 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 219 | fi 220 | 221 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 222 | 223 | exec "$JAVACMD" \ 224 | $MAVEN_OPTS \ 225 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 226 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 227 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 228 | -------------------------------------------------------------------------------- /jdtls.ext/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /jdtls.ext/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.shengchen.checkstyle 5 | parent 6 | ${base.name} :: Parent 7 | 1.4.2 8 | pom 9 | 10 | Java Checkstyle Runner 11 | UTF-8 12 | 2.7.2 13 | 9.0 14 | 15 | 16 | com.shengchen.checkstyle.runner 17 | com.shengchen.checkstyle.checker 18 | com.shengchen.checkstyle.target 19 | 20 | 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-failsafe-plugin 26 | 2.15 27 | 28 | 30 | 31 | integration-tests 32 | 33 | integration-test 34 | verify 35 | 36 | 37 | 39 | ${failsafeArgLine} 40 | 42 | ${skip.integration.tests} 43 | 44 | 45 | 46 | 47 | 48 | org.eclipse.tycho 49 | target-platform-configuration 50 | ${tycho-version} 51 | 52 | p2 53 | 54 | 55 | com.shengchen.checkstyle 56 | com.shengchen.checkstyle.target 57 | ${project.version} 58 | 59 | 60 | 61 | 62 | linux 63 | gtk 64 | x86_64 65 | 66 | 67 | win32 68 | win32 69 | x86_64 70 | 71 | 72 | macosx 73 | cocoa 74 | x86_64 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-checkstyle-plugin 84 | 3.1.2 85 | 86 | 87 | 88 | validate 89 | validate 90 | 91 | check 92 | 93 | 94 | 95 | 96 | 97 | com.puppycrawl.tools 98 | checkstyle 99 | ${checkstyle-version} 100 | 101 | 102 | 103 | ${project.parent.basedir}/config/checkstyle.xml 104 | checkstyle.header.file=${project.parent.basedir}/config/java.header 105 | true 106 | 107 | 108 | 109 | 110 | 111 | 112 | org.eclipse.tycho 113 | tycho-maven-plugin 114 | ${tycho-version} 115 | true 116 | 117 | 118 | 119 | 120 | 121 | oss.sonatype.org 122 | https://oss.sonatype.org/content/repositories/snapshots/ 123 | 124 | true 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-checkstyle", 3 | "displayName": "Checkstyle for Java", 4 | "description": "%description%", 5 | "version": "1.4.2", 6 | "author": "Sheng Chen", 7 | "publisher": "shengchen", 8 | "license": "GNU LGPL V3.0", 9 | "aiKey": "0558bc67-cc55-4730-9f4a-64b8f71393cb", 10 | "icon": "resources/icon_checkstyle.png", 11 | "homepage": "https://github.com/jdneo/vscode-checkstyle/blob/master/README.md", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/jdneo/vscode-checkstyle" 15 | }, 16 | "engines": { 17 | "vscode": "^1.64.0" 18 | }, 19 | "categories": [ 20 | "Programming Languages", 21 | "Linters" 22 | ], 23 | "keywords": [ 24 | "java", 25 | "checkstyle" 26 | ], 27 | "activationEvents": [ 28 | "onLanguage:java", 29 | "onCommand:java.checkstyle.quickFix", 30 | "onCommand:java.checkstyle.checkCode", 31 | "onCommand:java.checkstyle.setConfiguration" 32 | ], 33 | "main": "./dist/extension", 34 | "contributes": { 35 | "javaExtensions": [ 36 | "./server/com.shengchen.checkstyle.runner.jar" 37 | ], 38 | "menus": { 39 | "explorer/context": [ 40 | { 41 | "command": "java.checkstyle.checkCode", 42 | "when": "resourceLangId == java", 43 | "group": "Checkstyle@1" 44 | }, 45 | { 46 | "command": "java.checkstyle.checkCode", 47 | "when": "explorerResourceIsFolder", 48 | "group": "Checkstyle@1" 49 | }, 50 | { 51 | "command": "java.checkstyle.setConfiguration", 52 | "when": "!explorerResourceIsFolder && resourceLangId == xml", 53 | "group": "Checkstyle@2" 54 | } 55 | ] 56 | }, 57 | "commands": [ 58 | { 59 | "command": "java.checkstyle.checkCode", 60 | "title": "%contributes.commands.java.checkstyle.checkCode.title%", 61 | "category": "Checkstyle" 62 | }, 63 | { 64 | "command": "java.checkstyle.setConfiguration", 65 | "title": "%contributes.commands.java.checkstyle.setConfiguration.title%", 66 | "category": "Checkstyle" 67 | }, 68 | { 69 | "command": "java.checkstyle.setVersion", 70 | "title": "%contributes.commands.java.checkstyle.setVersion.title%", 71 | "category": "Checkstyle" 72 | } 73 | ], 74 | "configuration": { 75 | "title": "Checkstyle", 76 | "properties": { 77 | "java.checkstyle.configuration": { 78 | "type": "string", 79 | "description": "%configuration.java.checkstyle.configuration.description%", 80 | "scope": "resource" 81 | }, 82 | "java.checkstyle.properties": { 83 | "type": "object", 84 | "description": "%configuration.java.checkstyle.properties.description%", 85 | "scope": "resource" 86 | }, 87 | "java.checkstyle.version": { 88 | "type": "string", 89 | "description": "%configuration.java.checkstyle.version.description%", 90 | "default": "9.3", 91 | "scope": "resource" 92 | }, 93 | "java.checkstyle.modules": { 94 | "type": "array", 95 | "description": "%configuration.java.checkstyle.modules.description%", 96 | "default": [], 97 | "scope": "resource" 98 | }, 99 | "java.checkstyle.autocheck": { 100 | "type": "boolean", 101 | "description": "%configuration.java.checkstyle.autocheck.description%", 102 | "scope": "application", 103 | "default": true 104 | } 105 | } 106 | } 107 | }, 108 | "scripts": { 109 | "vscode:prepublish": "webpack --mode production", 110 | "compile": "tsc -p ./", 111 | "watch": "webpack --mode development --watch", 112 | "lint": "cross-env eslint 'src/**/*.ts' './scripts/build.js' './webpack.config.js' --format stylish", 113 | "build-plugin": "node scripts/build.js" 114 | }, 115 | "extensionDependencies": [ 116 | "redhat.java" 117 | ], 118 | "devDependencies": { 119 | "@types/fs-extra": "^9.0.13", 120 | "@types/lodash": "^4.14.178", 121 | "@types/node": "^14.18.9", 122 | "@types/node-fetch": "^2.5.12", 123 | "@types/vscode": "^1.64.0", 124 | "@typescript-eslint/eslint-plugin": "^7.1.0", 125 | "@typescript-eslint/parser": "^7.1.0", 126 | "cross-env": "^7.0.3", 127 | "eslint": "^8.57.0", 128 | "native-ext-loader": "^2.3.0", 129 | "ts-loader": "^9.2.6", 130 | "typescript": "^4.5.5", 131 | "webpack": "^5.94.0", 132 | "webpack-cli": "^4.9.2" 133 | }, 134 | "dependencies": { 135 | "fs-extra": "^10.0.0", 136 | "lodash": "^4.17.21", 137 | "node-fetch": "^2.6.7", 138 | "vscode-extension-telemetry-wrapper": "^0.11.1", 139 | "vscode-languageclient": "^6.1.3", 140 | "xml-js": "^1.6.11" 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /package.nls.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Provide real-time feedback about Checkstyle violations and quick fix actions", 3 | "contributes.commands.java.checkstyle.setConfiguration.title": "Set the Checkstyle Configuration File", 4 | "contributes.commands.java.checkstyle.setVersion.title": "Set the Checkstyle Version", 5 | "contributes.commands.java.checkstyle.checkCode.title": "Check Code with Checkstyle", 6 | "configuration.java.checkstyle.configuration.description": "Specify the path of the Checkstyle configuration file", 7 | "configuration.java.checkstyle.version.description": "Specify the version of Checkstyle", 8 | "configuration.java.checkstyle.modules.description": "Specify the third-party modules used for Checkstyle", 9 | "configuration.java.checkstyle.properties.description": "Specify the customized properties used in the Checkstyle configuration", 10 | "configuration.java.checkstyle.autocheck.description": "Specify if the extension will check the format automatically or not" 11 | } 12 | -------------------------------------------------------------------------------- /package.nls.zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "提供实时的 Checkstyle 代码检测结果,并帮助用户修改代码", 3 | "contributes.commands.java.checkstyle.setConfiguration.title": "设置 Checkstyle 配置文件", 4 | "contributes.commands.java.checkstyle.setVersion.title": "设置 Chekcstyle 版本", 5 | "contributes.commands.java.checkstyle.checkCode.title": "检查代码", 6 | "configuration.java.checkstyle.configuration.description": "Checkstyle 配置文件所在路径", 7 | "configuration.java.checkstyle.version.description": "使用的 Checkstyle 版本", 8 | "configuration.java.checkstyle.modules.description": "使用的第三方 Checkstyle 模块", 9 | "configuration.java.checkstyle.properties.description": "自定义 Checkstyle 配置文件中所用到的 Properties", 10 | "configuration.java.checkstyle.autocheck.description": "是否启用自动检查" 11 | } 12 | -------------------------------------------------------------------------------- /resources/icon_checkstyle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdneo/vscode-checkstyle/326c5b44877133088f7160660ac5a24007352c27/resources/icon_checkstyle.png -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | const path = require('path'); 5 | const https = require('https'); 6 | const fs = require('fs'); 7 | const fse = require('fs-extra'); 8 | const cp = require('child_process'); 9 | 10 | (async () => { 11 | await downloadCheckstyleJar(); 12 | buildBundle(); 13 | })(); 14 | 15 | async function downloadCheckstyleJar() { 16 | const downloadDir = path.join(__dirname, '..', 'server'); 17 | await fse.ensureDir(downloadDir); 18 | const checkstyleVersion = require('../package.json')['contributes']['configuration']['properties']['java.checkstyle.version']['default']; 19 | const dest = path.join(downloadDir, `checkstyle-${checkstyleVersion}-all.jar`); 20 | await download(`https://github.com/checkstyle/checkstyle/releases/download/checkstyle-${checkstyleVersion}/checkstyle-${checkstyleVersion}-all.jar`, dest); 21 | } 22 | 23 | async function download(url, dest) { 24 | return new Promise((resolve, reject) => { 25 | https.get(url, async function(response) { 26 | if (response.statusCode >= 200 && response.statusCode < 300) { 27 | const file = fs.createWriteStream(dest); 28 | file.on('finish', function() { 29 | file.close(); 30 | return resolve(); 31 | }); 32 | file.on('error', function(err) { 33 | fs.unlink(dest); 34 | reject(err); 35 | }); 36 | response.pipe(file); 37 | } else if (response.headers.location) { 38 | await download(response.headers.location, dest); 39 | return resolve(); 40 | } else { 41 | reject(new Error(response.statusCode + ' : ' + response.statusMessage)); 42 | } 43 | }); 44 | }); 45 | } 46 | 47 | function buildBundle() { 48 | const serverDir = path.join(__dirname, '..', 'jdtls.ext'); 49 | cp.execSync(`${mvnw()} clean verify`, { cwd: serverDir, stdio: [0, 1, 2] }); 50 | copy(path.join(serverDir, 'com.shengchen.checkstyle.checker/target'), path.resolve('server')); 51 | copy(path.join(serverDir, 'com.shengchen.checkstyle.runner/target'), path.resolve('server')); 52 | } 53 | 54 | function copy(sourceFolder, targetFolder) { 55 | const jars = fse.readdirSync(sourceFolder).filter(file => path.extname(file) === '.jar'); 56 | fse.ensureDirSync(targetFolder); 57 | for (const jar of jars) { 58 | // remove version from name 59 | const renamedJar = path.basename(jar).substring(0, path.basename(jar).lastIndexOf('-')) + '.jar'; 60 | fse.copyFileSync(path.join(sourceFolder, jar), path.join(targetFolder, renamedJar)); 61 | } 62 | } 63 | 64 | function isWin() { 65 | return /^win/.test(process.platform); 66 | } 67 | 68 | function mvnw() { 69 | return isWin() ? 'mvnw.cmd' : './mvnw'; 70 | } 71 | -------------------------------------------------------------------------------- /src/checkstyleChannel.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import { Disposable, OutputChannel, window } from 'vscode'; 5 | 6 | class CheckstyleChannel implements Disposable { 7 | private readonly channel: OutputChannel = window.createOutputChannel('Checkstyle'); 8 | 9 | public appendLine(message: string): void { 10 | this.channel.appendLine(message); 11 | } 12 | 13 | public show(): void { 14 | this.channel.show(); 15 | } 16 | 17 | public dispose(): void { 18 | this.channel.dispose(); 19 | } 20 | } 21 | 22 | export const checkstyleChannel: CheckstyleChannel = new CheckstyleChannel(); 23 | -------------------------------------------------------------------------------- /src/checkstyleDiagnosticCollector.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import { Diagnostic, DiagnosticCollection, DiagnosticSeverity, Disposable, languages, Range, Uri } from 'vscode'; 5 | import { ICheckstyleResult } from './models'; 6 | 7 | class CheckstyleDiagnosticCollector implements Disposable { 8 | private diagnosticCollection: DiagnosticCollection; 9 | 10 | constructor() { 11 | this.diagnosticCollection = languages.createDiagnosticCollection('Checkstyle'); 12 | } 13 | 14 | public addDiagnostics(uri: Uri, violations: ICheckstyleResult[]): void { 15 | const diagnostics: Diagnostic[] = []; 16 | for (const violation of violations) { 17 | if (violation.severity === 'ignore') { 18 | continue; // Do not report ignored diagnostics 19 | } 20 | const startLine: number = Math.max(violation.line - 1, 0); 21 | const startCharacter: number = Math.max(violation.column - 1, 0); 22 | diagnostics.push({ 23 | range: new Range(startLine, startCharacter, startLine + 1, 0), 24 | message: violation.message, 25 | severity: this.parseDiagnosticSeverity(violation.severity), 26 | source: 'Checkstyle', 27 | code: violation.sourceName, 28 | }); 29 | } 30 | this.diagnosticCollection.set(uri, diagnostics); 31 | } 32 | 33 | public diagnostics(uri: Uri): readonly Diagnostic[] | undefined { 34 | return this.diagnosticCollection.get(uri); 35 | } 36 | 37 | public getAllDiagnostics(): Diagnostic[][] { 38 | const allDiagnostics: Diagnostic[][] = []; 39 | this.diagnosticCollection.forEach((_uri: Uri, diagnostics: Diagnostic[]) => { 40 | allDiagnostics.push(diagnostics); 41 | }); 42 | return allDiagnostics; 43 | } 44 | 45 | public getResourceUris(): Uri[] { 46 | const uris: Uri[] = []; 47 | this.diagnosticCollection.forEach((uri: Uri) => uris.push(uri)); 48 | return uris; 49 | } 50 | 51 | public delete(uri: Uri): void { 52 | this.diagnosticCollection.delete(uri); 53 | } 54 | 55 | public clear(): void { 56 | this.diagnosticCollection.clear(); 57 | } 58 | 59 | public dispose(): void { 60 | if (this.diagnosticCollection) { 61 | this.diagnosticCollection.clear(); 62 | this.diagnosticCollection.dispose(); 63 | } 64 | } 65 | 66 | private parseDiagnosticSeverity(severity: ICheckstyleResult['severity']): DiagnosticSeverity { 67 | switch (severity) { 68 | case 'info': 69 | return DiagnosticSeverity.Information; 70 | case 'warning': 71 | return DiagnosticSeverity.Warning; 72 | case 'error': 73 | default: 74 | return DiagnosticSeverity.Error; 75 | } 76 | } 77 | 78 | } 79 | 80 | export const checkstyleDiagnosticCollector: CheckstyleDiagnosticCollector = new CheckstyleDiagnosticCollector(); 81 | -------------------------------------------------------------------------------- /src/checkstyleDiagnosticManager.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import * as _ from 'lodash'; 5 | import * as path from 'path'; 6 | import * as vscode from 'vscode'; 7 | import { checkstyleChannel } from './checkstyleChannel'; 8 | import { checkstyleDiagnosticCollector } from './checkstyleDiagnosticCollector'; 9 | import { checkstyleStatusBar } from './checkstyleStatusBar'; 10 | import { executeJavaLanguageServerCommand } from './commands/executeJavaLanguageServerCommand'; 11 | import { CheckstyleServerCommands } from './constants/commands'; 12 | import { FileSynchronizer, SyncedFile } from './features/FileSynchronizer'; 13 | import { ICheckstyleResult } from './models'; 14 | import { handleErrors } from './utils/errorUtils'; 15 | import { isAutoCheckEnabled } from './utils/settingUtils'; 16 | 17 | class CheckstyleDiagnosticManager implements vscode.Disposable { 18 | 19 | private context: vscode.ExtensionContext; 20 | private enabled: boolean; 21 | private listeners: vscode.Disposable[]; 22 | private pendingDiagnostics: Set; 23 | private syncedFiles: Map; 24 | private synchronizer: FileSynchronizer; 25 | private diagnosticDelayTrigger: () => Promise; 26 | 27 | public initialize(context: vscode.ExtensionContext): void { 28 | this.context = context; 29 | this.enabled = false; 30 | this.listeners = []; 31 | this.pendingDiagnostics = new Set(); 32 | this.syncedFiles = new Map(); 33 | this.synchronizer = new FileSynchronizer(this.context); 34 | this.diagnosticDelayTrigger = _.debounce(this.sendPendingDiagnostics.bind(this), 200); 35 | } 36 | 37 | public startListening(): void { 38 | if (this.listeners.length === 0 && this.enabled && isAutoCheckEnabled()) { 39 | vscode.workspace.onDidOpenTextDocument(this.onDidOpenTextDocument, this, this.listeners); 40 | vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, this.listeners); 41 | vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, this.listeners); 42 | vscode.workspace.textDocuments.forEach(this.onDidOpenTextDocument, this); 43 | } 44 | } 45 | 46 | public stopListening(): void { 47 | if (this.listeners.length !== 0) { 48 | for (const listener of this.listeners) { 49 | listener.dispose(); 50 | } 51 | this.listeners = []; 52 | this.synchronizer.dispose(); 53 | for (const file of this.syncedFiles.values()) { 54 | this.onDidCloseTextDocument(file.textDocument); 55 | } 56 | } 57 | } 58 | 59 | public activate(): void { 60 | this.enabled = true; 61 | this.startListening(); 62 | } 63 | 64 | public dispose(): void { 65 | this.enabled = false; 66 | this.stopListening(); 67 | } 68 | 69 | public getDiagnostics(uris: vscode.Uri[]): void { 70 | for (const uri of uris) { 71 | if (uri.scheme === 'file' && path.extname(uri.fsPath).toLowerCase() === '.java') { 72 | this.pendingDiagnostics.add(this.syncedFiles.get(uri.fsPath) || uri); 73 | } 74 | } 75 | if (uris.length !== 0) { 76 | this.diagnosticDelayTrigger(); 77 | } 78 | } 79 | 80 | public onDidChangeConfiguration(e: vscode.ConfigurationChangeEvent): void { 81 | if (e.affectsConfiguration('java.checkstyle.autocheck')) { 82 | if (isAutoCheckEnabled()) { 83 | this.startListening(); 84 | } else { 85 | this.stopListening(); 86 | } 87 | } 88 | } 89 | 90 | private onDidOpenTextDocument(document: vscode.TextDocument): void { 91 | if (!(document.languageId === 'java' && document.uri.scheme === 'file')) { 92 | return; 93 | } 94 | 95 | const filePath: string = document.uri.fsPath; 96 | if (this.syncedFiles.has(filePath)) { 97 | return; 98 | } 99 | 100 | const syncedFile: SyncedFile = new SyncedFile(document, this.synchronizer); 101 | this.syncedFiles.set(filePath, syncedFile); 102 | syncedFile.open(); 103 | this.requestDiagnostic(syncedFile); 104 | } 105 | 106 | private onDidChangeTextDocument(e: vscode.TextDocumentChangeEvent): void { 107 | const filePath: string = e.document.uri.fsPath; 108 | const syncedFile: SyncedFile | undefined = this.syncedFiles.get(filePath); 109 | if (!syncedFile) { 110 | return; 111 | } 112 | syncedFile.onContentChanged(e); 113 | this.requestDiagnostic(syncedFile); 114 | } 115 | 116 | private onDidCloseTextDocument(document: vscode.TextDocument): void { 117 | const filePath: string = document.uri.fsPath; 118 | const syncedFile: SyncedFile | undefined = this.syncedFiles.get(filePath); 119 | if (!syncedFile) { 120 | return; 121 | } 122 | this.syncedFiles.delete(filePath); 123 | syncedFile.close(); 124 | } 125 | 126 | private requestDiagnostic(file: SyncedFile): void { 127 | this.pendingDiagnostics.add(file); 128 | this.diagnosticDelayTrigger(); 129 | } 130 | 131 | private async sendPendingDiagnostics(): Promise { 132 | try { 133 | if (!this.enabled) { 134 | return; 135 | } 136 | 137 | await this.synchronizer.flush(); // Sync with the file system before sending 138 | 139 | const fileCheckMap: Map = new Map(); // Check path -> real uri 140 | for (const file of this.pendingDiagnostics.values()) { 141 | if (file instanceof SyncedFile) { 142 | fileCheckMap.set(file.syncUri.fsPath, file.realUri); 143 | } else { 144 | fileCheckMap.set(file.fsPath, file); 145 | } 146 | } 147 | 148 | fileCheckMap.forEach((uri: vscode.Uri) => checkstyleDiagnosticCollector.delete(uri)); 149 | 150 | const results: { [file: string]: ICheckstyleResult[] } | undefined = await executeJavaLanguageServerCommand<{ [file: string]: ICheckstyleResult[] }>( 151 | CheckstyleServerCommands.CHECK_CODE, [...fileCheckMap.keys()], 152 | ); 153 | if (!results) { 154 | checkstyleChannel.appendLine('Unable to get results from Language Server.'); 155 | return; 156 | } 157 | for (const [checkFile, diagnostics] of Object.entries(results)) { 158 | const diagnosticUri: vscode.Uri | undefined = fileCheckMap.get(checkFile); 159 | if (!diagnosticUri) { 160 | checkstyleChannel.appendLine(`Unable to map check file ${checkFile} back to real uri.`); 161 | continue; 162 | } 163 | checkstyleDiagnosticCollector.addDiagnostics(diagnosticUri, diagnostics); 164 | } 165 | checkstyleStatusBar.showStatus(); 166 | } catch (error) { 167 | handleErrors(error); 168 | } finally { 169 | this.pendingDiagnostics.clear(); 170 | } 171 | } 172 | } 173 | 174 | export const checkstyleDiagnosticManager: CheckstyleDiagnosticManager = new CheckstyleDiagnosticManager(); 175 | -------------------------------------------------------------------------------- /src/checkstyleStatusBar.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import { Disposable, StatusBarAlignment, StatusBarItem, window } from 'vscode'; 5 | import { checkstyleDiagnosticCollector } from './checkstyleDiagnosticCollector'; 6 | import { CheckstyleExtensionCommands } from './constants/commands'; 7 | 8 | class CheckstyleStatusBar implements Disposable { 9 | private statusBar: StatusBarItem; 10 | 11 | constructor() { 12 | this.statusBar = window.createStatusBarItem(StatusBarAlignment.Right); 13 | this.statusBar.show(); 14 | } 15 | 16 | public showStatus(): void { 17 | this.clearStatus(); 18 | let violations: number = 0; 19 | for (const diagnostics of checkstyleDiagnosticCollector.getAllDiagnostics()) { 20 | violations += diagnostics.length; 21 | } 22 | if (!violations) { 23 | this.statusBar.text = '$(check)'; 24 | this.statusBar.tooltip = '[Checkstyle] no violation found'; 25 | } else { 26 | this.statusBar.text = '$(bug)'; 27 | this.statusBar.tooltip = `[Checkstyle] ${violations} violation${violations === 1 ? '' : 's'} found`; 28 | this.statusBar.command = 'workbench.action.problems.focus'; 29 | } 30 | this.statusBar.show(); 31 | } 32 | 33 | public showError(reason?: string): void { 34 | this.statusBar.text = '$(stop)'; 35 | this.statusBar.tooltip = `[Checkstyle] ${reason || 'Internal error occurred'}`; 36 | this.statusBar.command = CheckstyleExtensionCommands.OPEN_OUTPUT_CHANNEL; 37 | this.statusBar.show(); 38 | } 39 | 40 | public dispose(): void { 41 | this.statusBar.dispose(); 42 | } 43 | 44 | private clearStatus(): void { 45 | this.statusBar.command = undefined; 46 | this.statusBar.text = ''; 47 | this.statusBar.tooltip = undefined; 48 | } 49 | } 50 | 51 | export const checkstyleStatusBar: CheckstyleStatusBar = new CheckstyleStatusBar(); 52 | -------------------------------------------------------------------------------- /src/commands/check.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import * as fse from 'fs-extra'; 5 | import { Uri, window, workspace } from 'vscode'; 6 | import { checkstyleDiagnosticManager } from '../checkstyleDiagnosticManager'; 7 | 8 | export async function checkCode(uri?: Uri): Promise { 9 | if (!uri) { // If not specified, check active editor 10 | if (!window.activeTextEditor) { 11 | return; 12 | } 13 | uri = window.activeTextEditor.document.uri; 14 | } 15 | let filesToCheck: Uri[]; 16 | if ((await fse.stat(uri.fsPath)).isDirectory()) { 17 | filesToCheck = await workspace.findFiles(`${workspace.asRelativePath(uri)}/**/*.java`); 18 | } else { 19 | filesToCheck = [uri]; 20 | } 21 | checkstyleDiagnosticManager.getDiagnostics(filesToCheck); 22 | } 23 | -------------------------------------------------------------------------------- /src/commands/config.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import * as fse from 'fs-extra'; 5 | import * as path from 'path'; 6 | import { Uri, window, WorkspaceFolder } from 'vscode'; 7 | import * as xmljs from 'xml-js'; 8 | import { checkstyleChannel } from '../checkstyleChannel'; 9 | import { BuiltinConfiguration, checkstyleDoctypeIds } from '../constants/checkstyleConfigs'; 10 | import { IQuickPickItemEx } from '../models'; 11 | import { setCheckstyleConfigurationPath } from '../utils/settingUtils'; 12 | import { findNonIgnoredFiles, getDefaultWorkspaceFolder, tryUseWorkspaceFolder } from '../utils/workspaceUtils'; 13 | 14 | export async function setConfiguration(uri?: Uri): Promise { 15 | if (uri) { 16 | if (path.extname(uri.fsPath).toLowerCase() === '.xml') { 17 | setCheckstyleConfigurationPath(tryUseWorkspaceFolder(uri.fsPath), uri); 18 | } else { 19 | window.showErrorMessage('Invalid Checkstyle configuration file'); 20 | return; 21 | } 22 | } else { 23 | const choice: string | undefined = await queryForConfiguration(); 24 | if (!choice) { 25 | return; 26 | } 27 | setCheckstyleConfigurationPath(choice); 28 | } 29 | window.showInformationMessage('Successfully set the Checkstyle configuration.'); 30 | } 31 | 32 | async function queryForConfiguration(): Promise { 33 | const detectedConfigurations: IQuickPickItemEx[] = await detectConfigurations(); 34 | const items: IQuickPickItemEx[] = [ 35 | ...detectedConfigurations, 36 | { 37 | label: BuiltinConfiguration.GoogleCheck, 38 | detail: "(Built-in) Google's Style", 39 | }, 40 | { 41 | label: BuiltinConfiguration.SunCheck, 42 | detail: "(Built-in) Sun's Style", 43 | }, 44 | { 45 | label: '$(link) Use URL...', 46 | detail: 'Use a Checkstyle configuration accessible via HTTP.', 47 | value: ':input', 48 | }, 49 | { 50 | label: '$(file-text) Browse...', 51 | detail: 'Select a configuration file from your file system.', 52 | value: ':browse', 53 | }, 54 | ]; 55 | const result: IQuickPickItemEx | undefined = await window.showQuickPick(items, { ignoreFocusOut: true }); 56 | if (!result) { 57 | return undefined; 58 | } else if (result.value === ':browse') { 59 | return await browseForConfiguration(); 60 | } else if (result.value === ':input') { 61 | return await inputConfiguration(); 62 | } else { 63 | return result.label; 64 | } 65 | } 66 | 67 | async function browseForConfiguration(): Promise { 68 | const workspaceFolder: WorkspaceFolder | undefined = getDefaultWorkspaceFolder(); 69 | const defaultUri: Uri | undefined = workspaceFolder && workspaceFolder.uri; 70 | const results: Uri[] | undefined = await window.showOpenDialog({ 71 | defaultUri, 72 | canSelectFiles: true, 73 | canSelectFolders: false, 74 | canSelectMany: false, 75 | openLabel: 'Select', 76 | filters: { 'Checkstyle Configuration': ['xml'] }, 77 | }); 78 | return results && results[0] && tryUseWorkspaceFolder(results[0].path); 79 | } 80 | 81 | async function inputConfiguration(): Promise { 82 | const configPath: string | undefined = await window.showInputBox({ 83 | prompt: 'Enter configuration URL here.', 84 | placeHolder: 'Supports http(s)://...', 85 | value: 'https://', 86 | ignoreFocusOut: true, 87 | }); 88 | return configPath && configPath.trim(); 89 | } 90 | 91 | async function detectConfigurations(): Promise { 92 | const detected: IQuickPickItemEx[] = []; 93 | for (const xml of await findNonIgnoredFiles('**/*.xml')) { 94 | const relativeXml: string = tryUseWorkspaceFolder(xml.fsPath); 95 | function doctypeFn(doctype: string): void { 96 | const [name, type] = doctype.split(/\s+/, 2); 97 | if (type.toUpperCase() === 'PUBLIC') { 98 | const pubid: string = doctype.match(/"(.+)"/)![1]; 99 | if (checkstyleDoctypeIds.includes(pubid)) { 100 | detected.push({ label: relativeXml, detail: xml.fsPath }); 101 | } 102 | } else if (type.toUpperCase() === 'SYSTEM') { 103 | if (name === 'module') { 104 | detected.push({ label: relativeXml, detail: xml.fsPath }); 105 | } 106 | } 107 | } 108 | try { 109 | xmljs.xml2js(await fse.readFile(xml.fsPath, 'utf8'), { doctypeFn }); 110 | } catch (error) { // Skip this xml, continue detecting process 111 | checkstyleChannel.appendLine(`Parse failed: ${xml}`); 112 | } 113 | } 114 | return detected; 115 | } 116 | -------------------------------------------------------------------------------- /src/commands/executeJavaLanguageServerCommand.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import { commands } from 'vscode'; 5 | import { JavaLanguageServerCommands } from '../constants/commands'; 6 | 7 | export function executeJavaLanguageServerCommand(...args: any[]): Thenable { 8 | return commands.executeCommand(JavaLanguageServerCommands.EXECUTE_WORKSPACE_COMMAND, ...args); 9 | } 10 | -------------------------------------------------------------------------------- /src/commands/fix.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import { Uri } from 'vscode'; 5 | import * as ls from 'vscode-languageserver-protocol'; 6 | import { checkstyleChannel } from '../checkstyleChannel'; 7 | import { CheckstyleServerCommands } from '../constants/commands'; 8 | import { applyWorkspaceEdit } from '../utils/editUtils'; 9 | import { handleErrors } from '../utils/errorUtils'; 10 | import { executeJavaLanguageServerCommand } from './executeJavaLanguageServerCommand'; 11 | 12 | export async function fixCheckstyleViolations(uri: Uri, offsets: number[], sourceNames: string[]): Promise { 13 | try { 14 | const workspaceEdit: ls.WorkspaceEdit | undefined = await executeJavaLanguageServerCommand( 15 | CheckstyleServerCommands.QUICK_FIX, uri.toString(), offsets, sourceNames); 16 | if (!workspaceEdit) { 17 | checkstyleChannel.appendLine('Unable to get quick fix item from Language Server.'); 18 | return; 19 | } 20 | await applyWorkspaceEdit(workspaceEdit); 21 | } catch (error) { 22 | handleErrors(error); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/commands/version.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import * as _ from 'lodash'; 5 | import { window } from 'vscode'; 6 | import { checkstyleConfigurationManager } from '../checkstyleConfigurationManager'; 7 | import { IQuickPickItemEx } from '../models'; 8 | import { setCheckstyleVersionString } from '../utils/settingUtils'; 9 | 10 | export async function setVersion(version?: string): Promise { 11 | version = version || await queryForVersion(); 12 | if (!version) { 13 | return; 14 | } 15 | setCheckstyleVersionString(version); 16 | } 17 | 18 | async function queryForVersion(): Promise { 19 | const result: IQuickPickItemEx | undefined = await window.showQuickPick([ 20 | ...await getRecommendedVersions(), 21 | { 22 | label: '$(file-text) All supported versions...', 23 | detail: 'List all the Checkstyle versions supported by extension.', 24 | value: ':list', 25 | }, 26 | ], { ignoreFocusOut: true }); 27 | if (!result) { 28 | return undefined; 29 | } else if (result.value === ':list') { 30 | try { 31 | return await window.showQuickPick(await getAllSupportedVersions(), { ignoreFocusOut: true }); 32 | } catch (error) { 33 | window.showQuickPick(['Network error']); 34 | return undefined; 35 | } 36 | } else { 37 | return result.value; 38 | } 39 | } 40 | 41 | async function getRecommendedVersions(): Promise { 42 | const versions: Map = new Map(); // version -> description 43 | function setDescription(version: string, description: string): void { 44 | versions.set(version, [...(versions.get(version) || []), description]); 45 | } 46 | try { // Do not set latest version if there's network issue 47 | setDescription(await getLatestVersion(), 'latest version'); 48 | } catch (error) { /* Skip */ } 49 | for (const version of await checkstyleConfigurationManager.getDownloadedVersions()) { 50 | setDescription(version, 'downloaded'); 51 | } 52 | setDescription(checkstyleConfigurationManager.getBuiltinVersion(), 'built-in'); 53 | const currentVersion: string | undefined = await checkstyleConfigurationManager.getCurrentVersion(); 54 | return sortVersions([...versions.keys()]).map((version: string) => ({ 55 | label: (version === currentVersion ? '$(check) ' : '') + version, 56 | description: versions.get(version)!.join(', '), 57 | value: version, 58 | })); 59 | } 60 | 61 | async function getLatestVersion(): Promise { 62 | const { tag_name } = await checkstyleConfigurationManager.fetchApiData<{ tag_name: string }>('/releases/latest'); 63 | return tag_name.match(/checkstyle-([\d.]+)/)![1]; 64 | } 65 | 66 | async function getAllSupportedVersions(): Promise { 67 | const tags: { ref: string }[] = await checkstyleConfigurationManager.fetchApiData('/git/refs/tags'); 68 | const versions: string[] = []; 69 | for (const { ref } of tags) { 70 | const match: RegExpMatchArray | null = ref.match(/checkstyle-([\d.]+)/); 71 | if (match) { 72 | versions.push(match[1]); 73 | } 74 | } 75 | return sortVersions(versions); 76 | } 77 | 78 | function sortVersions(versions: string[]): string[] { // Sort versions in descending order (latest first) 79 | return versions.sort((a: string, b: string) => b.localeCompare(a, undefined, { numeric: true })); 80 | } 81 | -------------------------------------------------------------------------------- /src/constants/checkstyleConfigs.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | export enum BuiltinConfiguration { 5 | GoogleCheck = '/google_checks.xml', 6 | SunCheck = '/sun_checks.xml', 7 | } 8 | 9 | export const checkstyleDoctypeIds: string[] = [ 10 | '-//Checkstyle//DTD Configuration 1.0//EN', 11 | '-//Checkstyle//DTD Configuration 1.1//EN', 12 | '-//Checkstyle//DTD Configuration 1.2//EN', 13 | '-//Checkstyle//DTD Configuration 1.3//EN', 14 | '-//Checkstyle//DTD Checkstyle Configuration 1.0//EN', 15 | '-//Checkstyle//DTD Checkstyle Configuration 1.1//EN', 16 | '-//Checkstyle//DTD Checkstyle Configuration 1.2//EN', 17 | '-//Checkstyle//DTD Checkstyle Configuration 1.3//EN', 18 | '-//Puppy Crawl//DTD Check Configuration 1.0//EN', 19 | '-//Puppy Crawl//DTD Check Configuration 1.1//EN', 20 | '-//Puppy Crawl//DTD Check Configuration 1.2//EN', 21 | '-//Puppy Crawl//DTD Check Configuration 1.3//EN', 22 | ]; 23 | -------------------------------------------------------------------------------- /src/constants/commands.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | export namespace JavaLanguageServerCommands { 5 | export const EXECUTE_WORKSPACE_COMMAND: string = 'java.execute.workspaceCommand'; 6 | } 7 | 8 | export namespace CheckstyleExtensionCommands { 9 | export const SET_CHECKSTYLE_CONFIGURATION: string = 'java.checkstyle.setConfiguration'; 10 | export const SET_CHECKSTYLE_VERSION: string = 'java.checkstyle.setVersion'; 11 | export const CHECK_CODE_WITH_CHECKSTYLE: string = 'java.checkstyle.checkCode'; 12 | export const FIX_CHECKSTYLE_VIOLATIONS: string = 'java.checkstyle.quickFix'; 13 | export const OPEN_OUTPUT_CHANNEL: string = 'java.checkstyle.open.output.channel'; 14 | } 15 | 16 | export namespace CheckstyleServerCommands { 17 | export const SET_CONFIGURATION: string = 'java.checkstyle.server.setConfiguration'; 18 | export const GET_VERSION: string = 'java.checkstyle.server.getVersion'; 19 | export const CHECK_CODE: string = 'java.checkstyle.server.checkCode'; 20 | export const QUICK_FIX: string = 'java.checkstyle.server.quickFix'; 21 | } 22 | 23 | export namespace VsCodeCommands { 24 | export const OPEN: string = 'vscode.open'; 25 | } 26 | -------------------------------------------------------------------------------- /src/constants/quickFix.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | export enum FixableCheck { 5 | // Blocks 6 | NeedBracesCheck = 'NeedBracesCheck', 7 | AvoidNestedBlocksCheck = 'AvoidNestedBlocksCheck', 8 | 9 | // Coding 10 | DefaultComesLastCheck = 'DefaultComesLastCheck', 11 | FinalLocalVariableCheck = 'FinalLocalVariableCheck', 12 | EmptyStatementCheck = 'EmptyStatementCheck', 13 | MissingSwitchDefaultCheck = 'MissingSwitchDefaultCheck', 14 | ExplicitInitializationCheck = 'ExplicitInitializationCheck', 15 | RequireThisCheck = 'RequireThisCheck', 16 | SimplifyBooleanReturnCheck = 'SimplifyBooleanReturnCheck', 17 | StringLiteralEqualityCheck = 'StringLiteralEqualityCheck', 18 | MultipleVariableDeclarationsCheck = 'MultipleVariableDeclarationsCheck', 19 | 20 | // Design 21 | DesignForExtensionCheck = 'DesignForExtensionCheck', 22 | FinalClassCheck = 'FinalClassCheck', 23 | 24 | // Modifier 25 | ModifierOrderCheck = 'ModifierOrderCheck', 26 | RedundantModifierCheck = 'RedundantModifierCheck', 27 | 28 | // Misc 29 | FinalParametersCheck = 'FinalParametersCheck', 30 | UncommentedMainCheck = 'UncommentedMainCheck', 31 | UpperEllCheck = 'UpperEllCheck', 32 | ArrayTypeStyleCheck = 'ArrayTypeStyleCheck', 33 | } 34 | -------------------------------------------------------------------------------- /src/constants/settings.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | export const JAVA_CHECKSTYLE_AUTOCHECK: string = 'java.checkstyle.autocheck'; 5 | export const JAVA_CHECKSTYLE_CONFIGURATION: string = 'java.checkstyle.configuration'; 6 | export const JAVA_CHECKSTYLE_PROPERTIES: string = 'java.checkstyle.properties'; 7 | export const JAVA_CHECKSTYLE_VERSION: string = 'java.checkstyle.version'; 8 | export const JAVA_CHECKSTYLE_MODULES: string = 'java.checkstyle.modules'; 9 | export const JAVA_CHECKSTYLE_CONFIGURATIONS: string[] = [ 10 | JAVA_CHECKSTYLE_CONFIGURATION, 11 | JAVA_CHECKSTYLE_PROPERTIES, 12 | JAVA_CHECKSTYLE_VERSION, 13 | JAVA_CHECKSTYLE_MODULES, 14 | ]; 15 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import { ConfigurationChangeEvent, Extension, ExtensionContext, extensions, FileSystemWatcher, languages, Uri, workspace } from 'vscode'; 5 | import { dispose as disposeTelemetryWrapper, initializeFromJsonFile, instrumentOperation, instrumentOperationAsVsCodeCommand } from 'vscode-extension-telemetry-wrapper'; 6 | import { checkstyleChannel } from './checkstyleChannel'; 7 | import { checkstyleConfigurationManager } from './checkstyleConfigurationManager'; 8 | import { checkstyleDiagnosticCollector } from './checkstyleDiagnosticCollector'; 9 | import { checkstyleDiagnosticManager } from './checkstyleDiagnosticManager'; 10 | import { checkstyleStatusBar } from './checkstyleStatusBar'; 11 | import { checkCode } from './commands/check'; 12 | import { setConfiguration } from './commands/config'; 13 | import { fixCheckstyleViolations } from './commands/fix'; 14 | import { setVersion } from './commands/version'; 15 | import { CheckstyleExtensionCommands } from './constants/commands'; 16 | import { quickFixProvider } from './quickFixProvider'; 17 | 18 | export async function activate(context: ExtensionContext): Promise { 19 | await initializeFromJsonFile(context.asAbsolutePath('./package.json'), { firstParty: true }); 20 | await instrumentOperation('activation', doActivate)(context); 21 | } 22 | 23 | export async function deactivate(): Promise { 24 | await disposeTelemetryWrapper(); 25 | } 26 | 27 | async function doActivate(_operationId: string, context: ExtensionContext): Promise { 28 | await waitForLsReady(); 29 | 30 | checkstyleDiagnosticManager.initialize(context); 31 | await checkstyleConfigurationManager.initialize(context); 32 | 33 | workspace.onDidChangeConfiguration((e: ConfigurationChangeEvent) => { 34 | checkstyleDiagnosticManager.onDidChangeConfiguration(e); 35 | checkstyleConfigurationManager.onDidChangeConfiguration(e); 36 | }); 37 | 38 | const codeWatcher: FileSystemWatcher = workspace.createFileSystemWatcher('**/*.{[jJ][aA][vV][aA]}', true /* ignoreCreateEvents */); 39 | codeWatcher.onDidDelete((uri: Uri) => { 40 | checkstyleDiagnosticCollector.delete(uri); 41 | }, null, context.subscriptions); 42 | 43 | context.subscriptions.push( 44 | checkstyleChannel, 45 | checkstyleStatusBar, 46 | checkstyleDiagnosticManager, 47 | checkstyleConfigurationManager, 48 | codeWatcher, 49 | languages.registerCodeActionsProvider({ scheme: 'file', language: 'java' }, quickFixProvider), 50 | instrumentOperationAsVsCodeCommand(CheckstyleExtensionCommands.OPEN_OUTPUT_CHANNEL, () => checkstyleChannel.show()), 51 | instrumentOperationAsVsCodeCommand(CheckstyleExtensionCommands.SET_CHECKSTYLE_CONFIGURATION, async (uri?: Uri) => await setConfiguration(uri)), 52 | instrumentOperationAsVsCodeCommand(CheckstyleExtensionCommands.SET_CHECKSTYLE_VERSION, async (version?: string) => await setVersion(version)), 53 | instrumentOperationAsVsCodeCommand(CheckstyleExtensionCommands.CHECK_CODE_WITH_CHECKSTYLE, async (uri?: Uri) => await checkCode(uri)), 54 | instrumentOperationAsVsCodeCommand(CheckstyleExtensionCommands.FIX_CHECKSTYLE_VIOLATIONS, async (uri: Uri, offsets: number[], sourceNames: string[]) => await fixCheckstyleViolations(uri, offsets, sourceNames)), 55 | ); 56 | } 57 | 58 | async function waitForLsReady(): Promise { 59 | const javaLanguageSupport: Extension | undefined = extensions.getExtension('redhat.java'); 60 | if (javaLanguageSupport?.isActive) { 61 | const extensionApi: any = javaLanguageSupport.exports; 62 | if (!extensionApi) { 63 | throw new Error('Failed to get the extension API from redhat.java'); 64 | } 65 | 66 | return extensionApi.serverReady(); 67 | } 68 | 69 | throw new Error('redhat.java is not installed or activated'); 70 | } 71 | -------------------------------------------------------------------------------- /src/features/FileSynchronizer.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import * as crypto from 'crypto'; 5 | import * as fse from 'fs-extra'; 6 | import * as os from 'os'; 7 | import * as path from 'path'; 8 | import * as vscode from 'vscode'; 9 | import { checkstyleChannel } from '../checkstyleChannel'; 10 | 11 | export class SyncedFile { 12 | constructor( 13 | private readonly document: vscode.TextDocument, 14 | private readonly synchronizer: FileSynchronizer, 15 | ) { } 16 | 17 | public get textDocument(): vscode.TextDocument { 18 | return this.document; 19 | } 20 | 21 | public get realUri(): vscode.Uri { 22 | return this.document.uri; 23 | } 24 | 25 | public get tempUri(): vscode.Uri | undefined { 26 | return this.synchronizer.getTempUri(this.realUri); 27 | } 28 | 29 | public get syncUri(): vscode.Uri { 30 | return this.tempUri || this.realUri; 31 | } 32 | 33 | public open(): void { 34 | if (this.document.isDirty) { 35 | this.synchronizer.open(this.document); 36 | } else { 37 | // Delay the temp file creation until changing it 38 | } 39 | } 40 | 41 | public close(): void { 42 | this.synchronizer.close(this.document); 43 | } 44 | 45 | public async onContentChanged(e: vscode.TextDocumentChangeEvent): Promise { 46 | if (!this.synchronizer.hasTempUri(this.realUri)) { // Lazy loading temp file 47 | this.synchronizer.open(this.document); 48 | } else { 49 | this.synchronizer.change(e.document, e.contentChanges); 50 | } 51 | } 52 | } 53 | 54 | interface ISyncRequests { 55 | write: Map; // Real file path -> new content 56 | remove: Set; // Real files to be closed and removed 57 | } 58 | 59 | // tslint:disable-next-line: max-classes-per-file 60 | export class FileSynchronizer implements vscode.Disposable { 61 | 62 | private tempHash: string = crypto.createHash('md5').update((new Date()).toTimeString()).digest('hex'); 63 | private tempStorage: string = this.getTempStorage(); 64 | private tempFilePool: Map = new Map(); // managed file path -> temp path 65 | private pendingRequests: ISyncRequests = { write: new Map(), remove: new Set() }; 66 | private pendingPromises: Map> = new Map(); 67 | 68 | constructor(private context: vscode.ExtensionContext) {} 69 | 70 | public dispose(): void { 71 | fse.remove(this.tempStorage); 72 | } 73 | 74 | public hasTempUri(realUri: vscode.Uri): boolean { 75 | return this.tempFilePool.has(realUri.fsPath); 76 | } 77 | 78 | public getTempUri(realUri: vscode.Uri): vscode.Uri | undefined { 79 | const tempPath: string | undefined = this.tempFilePool.get(realUri.fsPath); 80 | if (tempPath) { 81 | return vscode.Uri.file(tempPath); 82 | } 83 | return undefined; 84 | } 85 | 86 | // Ensure a file created in temp folder and managed by synchronizer 87 | public open(document: vscode.TextDocument): void { 88 | // Cancel pending remove request 89 | if (this.pendingRequests.remove.has(document.fileName)) { 90 | this.pendingRequests.remove.delete(document.fileName); 91 | } 92 | // Skip if already open 93 | if (!this.tempFilePool.has(document.fileName)) { 94 | this.pendingRequests.write.set(document.fileName, document.getText()); 95 | } 96 | } 97 | 98 | // Change content of temp file that already exists 99 | public change(document: vscode.TextDocument, _event?: readonly vscode.TextDocumentContentChangeEvent[]): void { 100 | // Skip if there's pending remove request 101 | if (this.pendingRequests.remove.has(document.fileName)) { 102 | return; 103 | } 104 | // Skip if not open 105 | if (this.tempFilePool.has(document.fileName)) { 106 | this.pendingRequests.write.set(document.fileName, document.getText()); 107 | } 108 | } 109 | 110 | // Delete the temp file, release the management of synchronizer 111 | public close(document: vscode.TextDocument): void { 112 | // Cancel the pending write request 113 | if (this.pendingRequests.write.has(document.fileName)) { 114 | this.pendingRequests.write.delete(document.fileName); 115 | } 116 | // Skip if already closed 117 | if (this.tempFilePool.has(document.fileName)) { 118 | this.pendingRequests.remove.add(document.fileName); 119 | } 120 | } 121 | 122 | // Do the actual IO operation, sending out all pending requests 123 | public async flush(): Promise { 124 | for (const [filePath, content] of this.pendingRequests.write.entries()) { 125 | this.setSyncPromise(filePath, async (tempPath: string) => { // When IO failure occurs, temp path will be updated 126 | await fse.ensureFile(tempPath); 127 | await fse.writeFile(tempPath, content); 128 | this.tempFilePool.set(filePath, tempPath); // Set or update temp path mapping 129 | }); 130 | } 131 | 132 | for (const filePath of this.pendingRequests.remove.values()) { 133 | this.setSyncPromise(filePath, async (tempPath: string) => { 134 | await fse.remove(tempPath); 135 | this.tempFilePool.delete(filePath); 136 | }); 137 | } 138 | 139 | this.pendingRequests = { write: new Map(), remove: new Set() }; 140 | await Promise.all(this.pendingPromises.values()); 141 | } 142 | 143 | private setSyncPromise(filePath: string, syncTask: (temp: string) => Promise): void { 144 | this.pendingPromises.set(filePath, (async (): Promise => { 145 | await this.pendingPromises.get(filePath); // Ensure IO sequence 146 | let tempPath: string = this.ensureTempPath(filePath); 147 | let failCount: number = 0; 148 | while (failCount < 5) { 149 | try { 150 | await syncTask(tempPath); 151 | break; 152 | } catch (error) { // Error in IO task, e.g. file occupied 153 | checkstyleChannel.appendLine(error.toString()); 154 | tempPath = this.ensureTempPath(tempPath); // Compute a new temp path 155 | failCount += 1; 156 | } 157 | } 158 | if (failCount >= 5) { 159 | vscode.window.showErrorMessage('Sync file IO error after 5 trials.'); 160 | } 161 | })()); 162 | } 163 | 164 | private ensureTempPath(realPath: string): string { 165 | let tempPath: string | undefined = this.tempFilePool.get(realPath); 166 | if (!tempPath) { 167 | tempPath = path.join(this.tempStorage, this.tempHash, vscode.workspace.asRelativePath(realPath, false /*includeWorkspaceFolder*/)); 168 | } 169 | return tempPath; 170 | } 171 | 172 | private getTempStorage(): string { 173 | const storagePath: string | undefined = this.context.storagePath; 174 | if (!storagePath) { 175 | return path.join(os.tmpdir(), `vscode_checkstyle_sync_${Math.random().toString(36).slice(2, 10)}`); 176 | } 177 | return path.join(storagePath, 'sync'); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/models.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | import { QuickPickItem } from 'vscode'; 4 | 5 | export interface IQuickPickItemEx extends QuickPickItem { 6 | value?: T; 7 | } 8 | 9 | export interface ICheckstyleResult { 10 | line: number; 11 | column: number; 12 | message: string; 13 | severity: 'ignore' | 'info' | 'warning' | 'error'; 14 | sourceName: string; 15 | } 16 | 17 | export interface ICheckstyleConfiguration { 18 | version: string; 19 | path: string; 20 | properties: object; 21 | modules: string[]; 22 | } 23 | -------------------------------------------------------------------------------- /src/quickFixProvider.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import * as _ from 'lodash'; 5 | import { CodeAction, CodeActionContext, CodeActionKind, CodeActionProvider, Diagnostic, Range, Selection, TextDocument, window } from 'vscode'; 6 | import { checkstyleDiagnosticCollector } from './checkstyleDiagnosticCollector'; 7 | import { CheckstyleExtensionCommands } from './constants/commands'; 8 | import { isQuickFixAvailable } from './utils/quickFixUtils'; 9 | 10 | class QuickFixProvider implements CodeActionProvider { 11 | public provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext): CodeAction[] { 12 | const diagnosticsByCheck: IDiagnosticsByCheck = groupDiagnosticsByCheck(context.diagnostics); 13 | const codeActions: CodeAction[] = []; 14 | for (const check of Object.keys(diagnosticsByCheck)) { 15 | if (!isQuickFixAvailable(check)) { 16 | continue; 17 | } 18 | const diagnostics: Diagnostic[] = diagnosticsByCheck[check]; 19 | codeActions.push(createFixAllDiagnostics(document, diagnostics, titleForDiagnostics(check, diagnostics), true)); 20 | } 21 | 22 | /* Fix all in selection */ 23 | const selection: Selection | undefined = window.activeTextEditor?.selection; 24 | if (codeActions.length > 1 && document.uri === window.activeTextEditor?.document?.uri && 25 | selection && !selection.isEmpty && range.contains(selection)) { 26 | const diagnostics: Diagnostic[] = fixableDiagnostics(context.diagnostics); 27 | if (diagnostics.length) { 28 | codeActions.push(createFixAllDiagnostics(document, diagnostics, 'Fix all auto-fixable Checkstyle violations in selection', false)); 29 | } 30 | } 31 | 32 | /* Fix all in document */ 33 | const allDiagnostics: readonly Diagnostic[] | undefined = checkstyleDiagnosticCollector.diagnostics(document.uri); 34 | if (allDiagnostics && allDiagnostics.length) { 35 | const diagnostics: Diagnostic[] = fixableDiagnostics(allDiagnostics); 36 | if (diagnostics.length) { 37 | codeActions.push(createFixAllDiagnostics(document, diagnostics, 'Fix all auto-fixable Checkstyle violations', false)); 38 | } 39 | } 40 | return codeActions; 41 | } 42 | } 43 | 44 | interface IDiagnosticsByCheck { 45 | [check: string]: Diagnostic[]; 46 | } 47 | 48 | function groupDiagnosticsByCheck(diagnostics: readonly Diagnostic[]): IDiagnosticsByCheck { 49 | const result: IDiagnosticsByCheck = {}; 50 | for (const diagnostic of diagnostics) { 51 | if (typeof diagnostic.code !== 'string') { 52 | continue; 53 | } 54 | if (!result[diagnostic.code]) { 55 | result[diagnostic.code] = []; 56 | } 57 | result[diagnostic.code].push(diagnostic); 58 | } 59 | return result; 60 | } 61 | 62 | export const quickFixProvider: QuickFixProvider = new QuickFixProvider(); 63 | 64 | function formatCheckstyleCheck(str: string): string { 65 | if (str.endsWith('Check')) { 66 | str = str.substring(0, str.length - 'Check'.length); 67 | } 68 | 69 | return _.startCase(str); 70 | } 71 | 72 | /** 73 | * Return the quick fix title for a group of diagnostics from a given check. 74 | */ 75 | function titleForDiagnostics(check: string, diagnostics: Diagnostic[]): string { 76 | if (diagnostics.length === 1) { 77 | return `Fix '${diagnostics[0].message}'`; 78 | } else { 79 | return `Fix ${diagnostics.length} '${formatCheckstyleCheck(check)}' Checkstyle violations`; 80 | } 81 | } 82 | 83 | function fixableDiagnostics(diagnostics: readonly Diagnostic[]): Diagnostic[] { 84 | return diagnostics.filter((diagnostic: Diagnostic) => isQuickFixAvailable(diagnostic.code)); 85 | } 86 | 87 | function createFixAllDiagnostics(document: TextDocument, diagnostics: Diagnostic[], title: string, diagnosticSpecific: boolean): CodeAction { 88 | return { 89 | title, 90 | diagnostics: diagnosticSpecific ? diagnostics : undefined, 91 | command: { 92 | title: 'Fix the Checkstyle violation', 93 | command: CheckstyleExtensionCommands.FIX_CHECKSTYLE_VIOLATIONS, 94 | arguments: [ 95 | document.uri, 96 | diagnostics.map((diagnostic: Diagnostic) => document.offsetAt(diagnostic.range.start)), 97 | diagnostics.map((diagnostic: Diagnostic) => diagnostic.code), 98 | ], 99 | }, 100 | kind: CodeActionKind.QuickFix, 101 | }; 102 | } 103 | -------------------------------------------------------------------------------- /src/utils/editUtils.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import { commands, Position, Selection, TextEdit, TextEditor, window, workspace, WorkspaceEdit } from 'vscode'; 5 | import * as protocolConverter from 'vscode-languageclient/lib/protocolConverter'; 6 | import { handleErrors } from './errorUtils'; 7 | 8 | const converter: protocolConverter.Converter = protocolConverter.createConverter(); 9 | 10 | export async function applyWorkspaceEdit(edit: any): Promise { 11 | const workspaceEdit: WorkspaceEdit = converter.asWorkspaceEdit(edit); 12 | if (workspaceEdit) { 13 | await workspace.applyEdit(workspaceEdit); 14 | // By executing the range formatting command to correct the indention according to the VS Code editor settings. 15 | // More details, see: https://github.com/redhat-developer/vscode-java/issues/557 16 | try { 17 | const currentEditor: TextEditor | undefined = window.activeTextEditor; 18 | // If the Uri path of the edit change is not equal to that of the active editor, we will skip the range formatting 19 | if (!currentEditor || currentEditor.document.uri.fsPath !== workspaceEdit.entries()[0][0].fsPath) { 20 | return; 21 | } 22 | const cursorPostion: Position = currentEditor.selection.active; 23 | // Get the array of all the changes 24 | const changes: TextEdit[] = workspaceEdit.entries()[0][1]; 25 | // Get the position information of the first change 26 | let startPosition: Position = new Position(changes[0].range.start.line, changes[0].range.start.character); 27 | let lineOffsets: number = changes[0].newText.split(/\r?\n/).length - 1; 28 | for (let i: number = 1; i < changes.length; i++) { 29 | // When it comes to a discontinuous range, execute the range formatting and record the new start position 30 | if (changes[i].range.start.line !== startPosition.line) { 31 | await executeRangeFormat(currentEditor, startPosition, lineOffsets); 32 | startPosition = new Position(changes[i].range.start.line, changes[i].range.start.character); 33 | lineOffsets = 0; 34 | } 35 | lineOffsets += changes[i].newText.split(/\r?\n/).length - 1; 36 | } 37 | await executeRangeFormat(currentEditor, startPosition, lineOffsets); 38 | // Recover the cursor's original position 39 | currentEditor.selection = new Selection(cursorPostion, cursorPostion); 40 | } catch (error) { 41 | handleErrors(error); 42 | } 43 | } 44 | } 45 | 46 | async function executeRangeFormat(editor: TextEditor, startPosition: Position, lineOffset: number): Promise { 47 | const endPosition: Position = editor.document.positionAt(editor.document.offsetAt(new Position(startPosition.line + lineOffset + 1, 0)) - 1); 48 | editor.selection = new Selection(startPosition, endPosition); 49 | await commands.executeCommand('editor.action.formatSelection'); 50 | } 51 | -------------------------------------------------------------------------------- /src/utils/errorUtils.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import * as vscode from 'vscode'; 5 | import { checkstyleChannel } from '../checkstyleChannel'; 6 | import { checkstyleConfigurationManager } from '../checkstyleConfigurationManager'; 7 | import { checkstyleStatusBar } from '../checkstyleStatusBar'; 8 | 9 | export async function handleErrors(error: Error): Promise { 10 | if (error['data']) { 11 | const message: string | undefined = error['data'].message; 12 | if (message && message.startsWith('cannot initialize module')) { 13 | handleModuleIntialization(message); 14 | } 15 | checkstyleChannel.appendLine(JSON.stringify(error['data'])); 16 | } else { 17 | checkstyleChannel.appendLine(error.stack || error.toString()); 18 | } 19 | 20 | checkstyleStatusBar.showError(); 21 | } 22 | 23 | async function handleModuleIntialization(message: string): Promise { 24 | const module: string = message.match(/cannot initialize module (.+?) -/)![1]; 25 | const choice: string | undefined = await vscode.window.showErrorMessage( 26 | `Module ${module} initialization failed. It may be caused by wrong configuraiton or incompatible version.`, 27 | 'Select another version', 'Open Configuration', 28 | ); 29 | if (choice === 'Select another version') { 30 | vscode.commands.executeCommand('java.checkstyle.setVersion'); 31 | } else if (choice === 'Open Configuration') { 32 | if (checkstyleConfigurationManager.configUri) { 33 | vscode.workspace.openTextDocument(checkstyleConfigurationManager.configUri).then((document: vscode.TextDocument) => { 34 | return vscode.window.showTextDocument(document); 35 | }); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/utils/quickFixUtils.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import { FixableCheck } from '../constants/quickFix'; 5 | 6 | export function isQuickFixAvailable(violationSourceName: any): boolean { 7 | if (violationSourceName && violationSourceName in FixableCheck) { 8 | return true; 9 | } 10 | return false; 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/settingUtils.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import { ConfigurationTarget, Uri, window, workspace, WorkspaceConfiguration } from 'vscode'; 5 | import { JAVA_CHECKSTYLE_AUTOCHECK, JAVA_CHECKSTYLE_CONFIGURATION, JAVA_CHECKSTYLE_MODULES, JAVA_CHECKSTYLE_PROPERTIES, JAVA_CHECKSTYLE_VERSION } from '../constants/settings'; 6 | import { resolveVariables } from './workspaceUtils'; 7 | 8 | export function setCheckstyleConfigurationPath(fsPath: string, uri?: Uri): void { 9 | setConfiguration(JAVA_CHECKSTYLE_CONFIGURATION, fsPath, uri); 10 | } 11 | 12 | export function getCheckstyleConfigurationPath(uri?: Uri): string { 13 | const configurationPath: string = getConfiguration(uri).get(JAVA_CHECKSTYLE_CONFIGURATION, ''); 14 | return resolveVariables(configurationPath, uri); 15 | } 16 | 17 | export function setCheckstyleVersionString(version: string, uri?: Uri): void { 18 | setConfiguration(JAVA_CHECKSTYLE_VERSION, version, uri); 19 | } 20 | 21 | export function getCheckstyleVersionString(uri?: Uri): string { 22 | const version: string = getConfiguration(uri).get(JAVA_CHECKSTYLE_VERSION)!; 23 | return resolveVariables(version, uri); 24 | } 25 | 26 | export function getCheckstyleExtensionModules(uri?: Uri): string[] { 27 | const modules: string[] = getConfiguration(uri).get(JAVA_CHECKSTYLE_MODULES)!; 28 | return modules.map((mod: string) => resolveVariables(mod, uri)); 29 | } 30 | 31 | export function getCheckstyleProperties(uri?: Uri): object { 32 | const properties: {} = getConfiguration(uri).get(JAVA_CHECKSTYLE_PROPERTIES, {}); 33 | for (const key of Object.keys(properties)) { 34 | properties[key] = resolveVariables(resolveVariables(properties[key], uri), uri); 35 | } 36 | return properties; 37 | } 38 | 39 | export function isAutoCheckEnabled(): boolean { 40 | return getConfiguration().get(JAVA_CHECKSTYLE_AUTOCHECK, true); 41 | } 42 | 43 | export function getConfiguration(uri?: Uri): WorkspaceConfiguration { 44 | return workspace.getConfiguration(undefined, uri || null); 45 | } 46 | 47 | function setConfiguration(section: string, value: any, uri?: Uri): void { 48 | if (!uri && window.activeTextEditor) { 49 | uri = window.activeTextEditor.document.uri; 50 | } 51 | getConfiguration(uri).update(section, value, ConfigurationTarget.WorkspaceFolder); 52 | } 53 | -------------------------------------------------------------------------------- /src/utils/workspaceUtils.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | import * as cp from 'child_process'; 5 | import * as _ from 'lodash'; 6 | import * as path from 'path'; 7 | import { Uri, window, workspace, WorkspaceFolder } from 'vscode'; 8 | import { checkstyleChannel } from '../checkstyleChannel'; 9 | 10 | export function getDefaultWorkspaceFolder(): WorkspaceFolder | undefined { 11 | if (workspace.workspaceFolders === undefined) { 12 | return undefined; 13 | } 14 | if (workspace.workspaceFolders.length === 1) { 15 | return workspace.workspaceFolders[0]; 16 | } 17 | if (window.activeTextEditor) { 18 | const activeWorkspaceFolder: WorkspaceFolder | undefined = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri); 19 | return activeWorkspaceFolder; 20 | } 21 | return undefined; 22 | } 23 | 24 | export function tryUseWorkspaceFolder(fsPath: string): string { 25 | const result: string = workspace.asRelativePath(fsPath); 26 | if (result === fsPath) { 27 | return result; 28 | } else { 29 | return path.join('${workspaceFolder}', result); 30 | } 31 | } 32 | 33 | const workspaceRegexp: RegExp = /\$\{workspacefolder\}/i; 34 | 35 | export function resolveVariables(value: string, resourceUri?: Uri): string { 36 | let workspaceFolder: WorkspaceFolder | undefined; 37 | if (resourceUri) { 38 | workspaceFolder = workspace.getWorkspaceFolder(resourceUri); 39 | } else { 40 | workspaceFolder = getDefaultWorkspaceFolder(); 41 | } 42 | if (workspaceRegexp.test(value)) { 43 | if (!workspaceFolder) { 44 | throw Error('No workspace folder is opened in current VS Code workspace when resolving ${workspaceFolder}'); // lgtm [js/template-syntax-in-string-literal] 45 | } 46 | return value.replace(workspaceRegexp, workspaceFolder.uri.fsPath); 47 | } 48 | return value; 49 | } 50 | 51 | // workspace.findFiles only defaults to exclude entires in files.exclude 52 | // so it is not even able to exclude node_modules 53 | // Refer to: https://github.com/Microsoft/vscode/issues/48674 54 | export async function findNonIgnoredFiles(pattern: string): Promise { 55 | let uris: Uri[] = await workspace.findFiles(pattern, `{${[ 56 | ...Object.keys(await workspace.getConfiguration('search', null).get('exclude') || {}), 57 | ...Object.keys(await workspace.getConfiguration('files', null).get('exclude') || {}), 58 | ].join(',')}}`); 59 | 60 | const workspaceFolder: WorkspaceFolder | undefined = getDefaultWorkspaceFolder(); 61 | if (workspaceFolder) { 62 | try { // tslint:disable-next-line: typedef 63 | const result: string = await new Promise((resolve, reject) => { 64 | cp.exec(`git check-ignore ${uris.map((uri: Uri) => workspace.asRelativePath(uri)).join(' ')}`, { 65 | cwd: workspaceFolder.uri.fsPath, 66 | }, (error: Error & { code?: 0 | 1 | 128 }, stdout: string, stderr: string) => { 67 | if (error && (error.code !== 0 && error.code !== 1)) { 68 | reject(error); 69 | } else if (stderr) { 70 | reject(new Error(stderr)); 71 | } else { 72 | resolve(stdout); 73 | } 74 | }); 75 | }); 76 | const excludes: Uri[] = result.trim().split('\n').map((relativePath: string) => { 77 | return Uri.file(path.join(workspaceFolder.uri.fsPath, relativePath.replace(/"(.+)"/, '$1'))); 78 | }); 79 | uris = _.differenceBy(uris, excludes, 'fsPath'); 80 | } catch (error) { 81 | checkstyleChannel.appendLine(`git check-ignore exec error: ${error.toString()}`); 82 | } 83 | } 84 | 85 | return uris; 86 | } 87 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2020", 5 | "outDir": "out", 6 | "lib": ["es2020"], 7 | "sourceMap": true, 8 | "rootDir": "src", 9 | "noUnusedLocals": true, 10 | "noImplicitThis": true, 11 | "noImplicitReturns": true, 12 | "noUnusedParameters": true, 13 | "strictNullChecks": true, 14 | "alwaysStrict": true 15 | }, 16 | "exclude": ["node_modules", ".vscode-test"] 17 | } 18 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) jdneo. All rights reserved. 2 | // Licensed under the GNU LGPLv3 license. 3 | 4 | //@ts-check 5 | 6 | 'use strict'; 7 | 8 | const path = require('path'); 9 | 10 | /**@type {import('webpack').Configuration}*/ 11 | const config = { 12 | target: 'node', 13 | node: { 14 | __dirname: false, 15 | __filename: false, 16 | }, 17 | entry: './src/extension.ts', 18 | output: { 19 | path: path.resolve(__dirname, 'dist'), 20 | filename: 'extension.js', 21 | libraryTarget: 'commonjs2', 22 | devtoolModuleFilenameTemplate: '../[resource-path]', 23 | }, 24 | externals: { 25 | 'applicationinsights-native-metrics': 'commonjs applicationinsights-native-metrics', 26 | vscode: 'commonjs vscode', 27 | }, 28 | devtool: 'source-map', 29 | resolve: { 30 | extensions: ['.ts', '.js'], 31 | }, 32 | module: { 33 | rules: [{ 34 | test: /\.ts$/, 35 | exclude: /node_modules/, 36 | use: [{ 37 | loader: 'ts-loader', 38 | }] 39 | }, { 40 | test: /\.node$/, 41 | loader: 'native-ext-loader', 42 | }] 43 | }, 44 | } 45 | module.exports = config; 46 | --------------------------------------------------------------------------------