├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE.md ├── scripts │ └── check_and_update_jdk.py └── workflows │ ├── bump-jdk.yml │ ├── conflictDetector.yaml │ ├── duplicate-issues-detector.yaml │ ├── no-response.yml │ ├── pr-verify.yml │ └── release.yml ├── .gitignore ├── .sdkmanrc ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── DCO ├── LICENSE ├── README.md ├── USAGE_DATA.md ├── document ├── _java.learnMoreAboutCleanUps.md ├── _java.learnMoreAboutRefactorings.md ├── _java.metadataFilesGeneration.md ├── _java.notCoveredExecution.md ├── _java.templateVariables.md ├── refactoring_change_signature.png └── refactoring_menu.png ├── formatters └── eclipse-formatter.xml ├── gulpfile.js ├── icons └── icon128.png ├── images ├── changelog │ ├── ChooseProduct.png │ ├── ClientPort.png │ ├── CreateNewConfiguration.png │ ├── DebugConfigurationOpen.png │ ├── DebugRemoteServer.png │ ├── RemoteServer.png │ ├── RemoteServerInVSCode.png │ ├── SocketSteamInVSCode.png │ ├── importMavenProject.png │ ├── importProject.png │ ├── importedMavenProject.png │ ├── loadingTargetPlatform.png │ ├── reloadTargetPlatform.png │ └── setTargetPlatform.png ├── statusMarker.png └── vscode-java.0.0.1.gif ├── language-support ├── html │ └── inline-html.json ├── java │ ├── java.tmLanguage.json │ └── language-configuration.json ├── json │ └── inline-json.json ├── kotlin │ ├── kotlin.tmLanguage.json │ └── language-configuration.json ├── properties │ ├── JavaProperties.tmLanguage.json │ └── java-properties-configuration.json ├── sql │ └── inline-sql.json ├── xml │ └── inline-xml.json └── yaml │ └── inline-yaml.json ├── package-lock.json ├── package.json ├── package.nls.json ├── package.nls.ko.json ├── package.nls.zh-cn.json ├── package.nls.zh-tw.json ├── schemas └── package.schema.json ├── snippets └── server.json ├── src ├── TracingLanguageClient.ts ├── apiManager.ts ├── buildFilesSelector.ts ├── buildpath.ts ├── clientCodeActionProvider.ts ├── clientErrorHandler.ts ├── codeActionProvider.ts ├── commands.ts ├── diagnostic.ts ├── documentSymbols.ts ├── errorUtils.ts ├── extension.api.ts ├── extension.ts ├── fileEventHandler.ts ├── goToDefinition.ts ├── gradle │ └── gradleCodeActionProvider.ts ├── hoverAction.ts ├── javaClassEditor.ts ├── javaServerStarter.ts ├── jdkUtils.ts ├── languageStatusItemFactory.ts ├── log.ts ├── lombokSupport.ts ├── markdownPreviewProvider.ts ├── outline │ ├── extendedOutlineQuickPick.ts │ └── protocol.ts ├── outputInfoCollector.ts ├── pasteAction.ts ├── pasteEventHandler.ts ├── plugin.ts ├── pom │ └── pomCodeActionProvider.ts ├── protocol.ts ├── providerDispatcher.ts ├── refactorAction.ts ├── refactoring │ ├── changeSignaturePanel.ts │ └── extractInterface.ts ├── requirements.ts ├── runtimeStatusBarProvider.ts ├── serverStatus.ts ├── serverStatusBarProvider.ts ├── serverTaskPresenter.ts ├── serverTasks.ts ├── settings.ts ├── smartSemicolonDetection.ts ├── snippetCompletionProvider.ts ├── sourceAction.ts ├── standardLanguageClient.ts ├── standardLanguageClientUtils.ts ├── syntaxLanguageClient.ts ├── telemetry.ts ├── themeUtils.ts ├── typeHierarchy │ ├── model.ts │ ├── protocol.ts │ ├── references-view.d.ts │ ├── typeHierarchyTree.ts │ └── util.ts ├── utils.ts ├── webview │ ├── changeSignature │ │ ├── App.css │ │ ├── App.tsx │ │ └── index.tsx │ └── vscodeApiWrapper.ts └── webviewUtils.ts ├── test ├── Test Plan.md ├── common.ts ├── lightweight-mode-suite │ ├── extension.test.ts │ ├── index.ts │ └── publicApi.test.ts ├── resources │ ├── packageExample.json │ └── projects │ │ ├── eclipse │ │ └── simple-app │ │ │ ├── .classpath │ │ │ ├── .project │ │ │ ├── .settings │ │ │ └── org.eclipse.jdt.core.prefs │ │ │ └── src │ │ │ └── app │ │ │ ├── App.java │ │ │ ├── Bar.java │ │ │ └── Foo.java │ │ ├── gradle │ │ ├── gradle-11 │ │ │ ├── build.gradle │ │ │ ├── gradle │ │ │ │ └── wrapper │ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ ├── settings.gradle │ │ │ └── src │ │ │ │ └── main │ │ │ │ └── java │ │ │ │ └── foo │ │ │ │ └── bar │ │ │ │ └── Foo.java │ │ └── simple-gradle │ │ │ ├── build.gradle │ │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ ├── settings.gradle │ │ │ └── src │ │ │ └── main │ │ │ └── java │ │ │ └── foo │ │ │ └── bar │ │ │ └── Foo.java │ │ ├── maven │ │ ├── multimodule │ │ │ ├── module1 │ │ │ │ ├── pom.xml │ │ │ │ └── src │ │ │ │ │ └── main │ │ │ │ │ └── java │ │ │ │ │ └── module1 │ │ │ │ │ └── Foo.java │ │ │ ├── module2 │ │ │ │ ├── pom.xml │ │ │ │ └── src │ │ │ │ │ └── main │ │ │ │ │ └── java │ │ │ │ │ └── module2 │ │ │ │ │ └── Foo.java │ │ │ └── pom.xml │ │ ├── salut-java11 │ │ │ ├── pom.xml │ │ │ └── src │ │ │ │ └── main │ │ │ │ └── java │ │ │ │ └── org │ │ │ │ └── sample │ │ │ │ └── Bar.java │ │ └── salut │ │ │ ├── pom.xml │ │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ ├── java │ │ │ │ ├── Foo.java │ │ │ │ ├── Foo2.java │ │ │ │ └── Foo3.java │ │ │ └── org │ │ │ │ └── sample │ │ │ │ ├── Bar.java │ │ │ │ └── TestJavadoc.java │ │ │ └── resources │ │ │ └── test.properties │ │ └── singlefile │ │ ├── Single.java │ │ └── WithError.java ├── runtest.ts └── standard-mode-suite │ ├── codeActionProvider.test.ts │ ├── extension.test.ts │ ├── gotoSuperImplementation.test.ts │ ├── index.ts │ ├── projects.test.ts │ ├── publicApi.test.ts │ ├── rename.test.ts │ ├── snippetCompletionProvider.test.ts │ └── utils.test.ts ├── tsconfig.base.json ├── tsconfig.json ├── tsconfig.webview.json ├── vscode.proposed.documentPaste.d.ts ├── webpack.config.js └── webview-resources ├── button.css ├── document.css ├── highlight.css └── markdown.css /.editorconfig: -------------------------------------------------------------------------------- 1 | # Tab indentation 2 | [*] 3 | indent_style = tab 4 | indent_size = 4 5 | trim_trailing_whitespace = true 6 | 7 | [{.travis.yml,npm-shrinkwrap.json,package.json}] 8 | indent_style = space 9 | indent_size = 2 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # .gitignore 2 | out 3 | server 4 | node_modules 5 | *.vsix 6 | .DS_Store 7 | .vscode-test 8 | undefined 9 | target 10 | dist 11 | jre 12 | lombok 13 | bin/ 14 | .settings 15 | .classpath 16 | .project 17 | test/resources/projects/**/.vscode 18 | test/resources/projects/maven/salut/testGradle 19 | test-temp 20 | 21 | # specific to eslint 22 | vscode*.d.ts -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "parser": "@typescript-eslint/parser", 7 | "parserOptions": { 8 | "ecmaVersion": "latest", 9 | "project": [ 10 | "tsconfig.webview.json", 11 | "tsconfig.json" 12 | ] 13 | }, 14 | "plugins": [ 15 | "@typescript-eslint" 16 | ], 17 | "rules": { 18 | "@typescript-eslint/member-delimiter-style": [ 19 | "error", 20 | { 21 | "multiline": { 22 | "delimiter": "semi", 23 | "requireLast": true 24 | }, 25 | "singleline": { 26 | "delimiter": "semi", 27 | "requireLast": false 28 | } 29 | } 30 | ], 31 | "@typescript-eslint/naming-convention": "error", 32 | "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error", 33 | "@typescript-eslint/prefer-for-of": "error", 34 | "@typescript-eslint/semi": [ 35 | "error", 36 | "always" 37 | ], 38 | "@typescript-eslint/type-annotation-spacing": "error", 39 | "curly": [ 40 | "error", 41 | "multi-line" 42 | ], 43 | "eqeqeq": [ 44 | "error", 45 | "always" 46 | ], 47 | "id-denylist": [ 48 | "error", 49 | "any", 50 | "Number", 51 | "number", 52 | "String", 53 | "string", 54 | "Boolean", 55 | "boolean", 56 | "Undefined", 57 | "undefined" 58 | ], 59 | "id-match": "error", 60 | "no-debugger": "error", 61 | "no-multiple-empty-lines": "error", 62 | "no-trailing-spaces": "error", 63 | "no-underscore-dangle": "error", 64 | "no-var": "error", 65 | "prefer-arrow-callback": [ 66 | "error", 67 | { 68 | "allowNamedFunctions": true 69 | } 70 | ], 71 | "prefer-const": "error", 72 | "prefer-template": "error", 73 | "quote-props": [ 74 | "error", 75 | "as-needed" 76 | ], 77 | "semi": "error", 78 | "spaced-comment": [ 79 | "error", 80 | "always", 81 | { 82 | "markers": [ 83 | "/" 84 | ] 85 | } 86 | ] 87 | }, 88 | "overrides": [ 89 | { 90 | "files": [ 91 | "**/*.js" 92 | ], 93 | "rules": { 94 | "@typescript-eslint/no-var-requires": "off", 95 | "@typescript-eslint/naming-convention": "off", 96 | "@typescript-eslint/semi": "off", 97 | "prefer-arrow/prefer-arrow-functions": "off", 98 | "prefer-arrow-callback": "off", 99 | "no-useless-escape": "off", 100 | "spaced-comment": "off", 101 | "semi": "off", 102 | "prefer-template": "off", 103 | "prefer-const": "off" 104 | } 105 | }, 106 | { 107 | "files": [ 108 | "**/*.test.ts" 109 | ], 110 | "rules": { 111 | "prefer-arrow-callback": "off" 112 | } 113 | } 114 | ] 115 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | [provide a description of the issue] 7 | 8 | ##### Environment 9 | - Operating System: 10 | - JDK version: 11 | - Visual Studio Code version: 12 | - Java extension version: 13 | 14 | ##### Steps To Reproduce 15 | 1. [step 1] 16 | 2. [step 2] 17 | 18 | [Please attach a sample project reproducing the error] 19 | [Please attach logs](https://github.com/redhat-developer/vscode-java/wiki/Troubleshooting#enable-logging) 20 | 21 | ##### Current Result 22 | 23 | ##### Expected Result 24 | 25 | ##### Additional Information 26 | -------------------------------------------------------------------------------- /.github/scripts/check_and_update_jdk.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re 4 | import requests 5 | import json 6 | import ast 7 | 8 | readme_ver_pattern = r'(?:(?<=Supports code from Java 1\.8 to Java )(\d+)|(?<=JavaSE-)~|(?<=path/to/jdk-)~)' # After resolving current_jdk, we will replace ~ 9 | 10 | # Query the Oracle website for the latest JDK version 11 | response = requests.get('http://javadl-esd-secure.oracle.com/update/baseline.version') 12 | latest_jdk = re.search(r'(?P\d+)\.?', response.text) 13 | if latest_jdk is None: 14 | print('Failed to retrieve latest JDK version') 15 | exit(1) 16 | latest_jdk = latest_jdk.group('major') 17 | print(f'Latest JDK version: {latest_jdk}') 18 | 19 | # Query the vscode-java repo for the current supported JDK version 20 | with open('README.md', 'r') as f: # Open the README.md file in read mode 21 | readme = f.read() # Read the file content as a string 22 | current_jdk = re.search(readme_ver_pattern, readme) # Search for the JDK version in the string 23 | if current_jdk is None: 24 | print('Failed to retrieve current JDK version') 25 | exit(1) 26 | current_jdk = current_jdk.group(1) 27 | print(f'Current supported JDK version: {current_jdk}') 28 | 29 | # If the latest JDK version is not the same as the current supported JDK version, check the test status and update the files 30 | if latest_jdk != current_jdk: 31 | print(f'New JDK version detected: {latest_jdk}') 32 | # Create a formatted string template from the URI structure 33 | uri_base = 'https://ci.eclipse.org/ls/job/jdt-ls-main/lastCompletedBuild/testReport/org.eclipse.jdt.ls.core.internal.{package}/{java_class}/{method}/api/python' 34 | # Define the test URLs to check using the template and list comprehension 35 | tests = [ 36 | uri_base.format(package='managers', java_class=c, method=m) for c, m in [('EclipseProjectImporterTest', 'testPreviewFeaturesDisabledByDefault'), ('InvisibleProjectImporterTest', 'testPreviewFeaturesEnabledByDefault'), ('MavenProjectImporterTest', f'testJava{latest_jdk}Project')] 37 | ] 38 | 39 | # Check the test status for each test URL 40 | all_tests_passed = True 41 | for i in range(len(tests)): 42 | response = requests.get(tests[i]) 43 | if not response.ok: 44 | print(f'Test #{i + 1} not found ({tests[i]})') 45 | all_tests_passed = False 46 | break 47 | data = ast.literal_eval(response.text) # Use ast.literal_eval, because response.json() fails 48 | try: 49 | if data['status'] != 'PASSED': 50 | print(f'Test #{i + 1} failed ({tests[i]})') 51 | all_tests_passed = False 52 | break 53 | except KeyError: 54 | print(f'Test #{i + 1} not found ({tests[i]})') 55 | all_tests_passed = False 56 | break 57 | 58 | # If all tests passed, update the README.md and the package.json files 59 | if all_tests_passed: 60 | print('All tests passed') 61 | 62 | # Replace the ~ with current_jdk 63 | readme_ver_pattern = re.sub('~', current_jdk, readme_ver_pattern) 64 | 65 | # Write this to a file for the create-pull-request workflow 66 | with open('latest_jdk.txt', 'w') as f: 67 | f.write(latest_jdk) 68 | 69 | # Replace the current supported JDK version with the latest JDK version 70 | readme = re.sub(readme_ver_pattern, latest_jdk, readme) 71 | 72 | # Write the updated README.md file 73 | with open('README.md', 'w') as f: 74 | f.write(readme) 75 | 76 | # Read the package.json file 77 | with open('package.json', 'r') as f: 78 | package = json.load(f) 79 | 80 | # Add the latest JDK version to the java.configuration.runtimes array 81 | jdks_config = next(filter(lambda e: e['id'] == "java-jdks", package['contributes']['configuration'])) 82 | jdks_config['properties']['java.configuration.runtimes']['items']['properties']['name']['enum'].append(f'JavaSE-{latest_jdk}') 83 | 84 | # Write the updated package.json file 85 | with open('package.json', 'w') as f: 86 | json.dump(package, f, indent=2) 87 | else: 88 | print('Some tests failed, aborting update') 89 | exit(1) 90 | else: 91 | print('No new JDK version detected, nothing to do') 92 | exit(0) 93 | exit(0) 94 | -------------------------------------------------------------------------------- /.github/workflows/bump-jdk.yml: -------------------------------------------------------------------------------- 1 | name: Update JDK Version 2 | on: 3 | schedule: 4 | - cron: '0 10 * * *' 5 | workflow_dispatch: 6 | jobs: 7 | update-jdk-version: 8 | runs-on: ubuntu-latest 9 | env: 10 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v4 14 | 15 | - name: Set up Python 16 | uses: actions/setup-python@v4 17 | with: 18 | python-version: '3.x' 19 | 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install requests 24 | 25 | - name: Check and update JDK version 26 | run: | 27 | if [ -f latest_jdk.txt ]; then 28 | rm latest_jdk.txt # Remove previous records 29 | fi 30 | python .github/scripts/check_and_update_jdk.py 31 | 32 | - name: Read latest JDK version from file 33 | run: | 34 | if [ -f latest_jdk.txt ]; then 35 | version=$(cat latest_jdk.txt) 36 | echo "Latest JDK version: $version" 37 | echo "latest_jdk=$version" >> $GITHUB_ENV # set the latest_jdk environment variable 38 | else 39 | echo "No new JDK version detected, nothing to do" 40 | exit 0 41 | fi 42 | 43 | - name: Check for existing PR 44 | id: check_pr 45 | run: | 46 | pr_number=$(gh pr list --search "Found JavaSE version ${{ env.latest_jdk }}" --json number --jq '.[0].number') 47 | echo "pr_number=$pr_number" >> $GITHUB_ENV 48 | 49 | - name: Branch and push changes 50 | if: ${{ success() && env.latest_jdk != '' && steps.check_pr.outputs.pr_number == '' }} 51 | run: | 52 | git config --global user.email "redhattools-bot@users.noreply.github.com" 53 | git config --global user.name "redhattools-bot" 54 | git checkout -b "update-jdk-${{ env.latest_jdk }}" 55 | git commit -am "Bump JDK to ${{ env.latest_jdk }}" 56 | git push origin "update-jdk-${{ env.latest_jdk }}" 57 | gh pr create --title "Found JavaSE version ${{ env.latest_jdk }}" --body "See [Raw logs](https://github.com/${{ github.repository }}/commit/${{ github.sha }}/checks/${{ github.check_run_id }}/logs)" 58 | 59 | -------------------------------------------------------------------------------- /.github/workflows/conflictDetector.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | jobs: 6 | triage: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: mschilde/auto-label-merge-conflicts@8c6faa8a252e35ba5e15703b3d747bf726cdb95c 10 | with: 11 | CONFLICT_LABEL_NAME: "has conflicts" 12 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 13 | -------------------------------------------------------------------------------- /.github/workflows/duplicate-issues-detector.yaml: -------------------------------------------------------------------------------- 1 | name: Potential Duplicate Issues 2 | on: 3 | issues: 4 | types: [opened, edited] #edited means the issue title changed 5 | jobs: 6 | run: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: wow-actions/potential-duplicates@4d4ea0352e0383859279938e255179dd1dbb67b5 #v1.1.0 10 | with: 11 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 12 | # Issue title filter work with anymatch https://www.npmjs.com/package/anymatch. 13 | # Any matched issue will stop detection immediately. 14 | # You can specify multi filters in each line. 15 | filter: '' 16 | # Exclude keywords in title before detecting. 17 | exclude: 'regression' 18 | # Label to set, when potential duplicates are detected. 19 | label: potential-duplicate 20 | # Get issues with state to compare. Supported state: 'all', 'closed', 'open'. 21 | state: all 22 | # If similarity is higher than this threshold([0,1]), issue will be marked as duplicate. 23 | threshold: 0.6 24 | # Reactions to be add to comment when potential duplicates are detected. 25 | # Available reactions: "-1", "+1", "confused", "laugh", "heart", "hooray", "rocket", "eyes" 26 | reactions: 'confused' 27 | # Comment to post when potential duplicates are detected. 28 | comment: | 29 | We have found issues that are potential duplicates: {{#issues}} 30 | - #{{ number }} ({{ accuracy }}%) 31 | {{/issues}} 32 | 33 | If any of the issues listed above are a duplicate, please consider closing this issue & upvoting/commenting the original one. 34 | Alternatively, if neither of the listed issues addresses your feature/bug, keep this issue open. -------------------------------------------------------------------------------- /.github/workflows/no-response.yml: -------------------------------------------------------------------------------- 1 | name: No Response 2 | 3 | # **What it does**: Closes issues where the original author doesn't respond to a request for information. 4 | # **Why we have it**: To remove the need for maintainers to remember to check back on issues periodically to see if contributors have responded. 5 | 6 | on: 7 | issue_comment: 8 | types: [created] 9 | schedule: 10 | - cron: '0 10 * * *' 11 | 12 | jobs: 13 | noResponse: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: lee-dohm/no-response@9bb0a4b5e6a45046f00353d5de7d90fb8bd773bb #v0.5.0 17 | with: 18 | token: ${{ github.token }} 19 | daysUntilClose: 30 # Number of days of inactivity before an Issue is closed for lack of response 20 | responseRequiredLabel: "need info" # Label indicating that a response from the original author is required 21 | closeComment: > 22 | This issue has been closed automatically because it needs more information and has not had recent activity. Please reach out if you have or find the answers we need so that we can investigate further. 23 | -------------------------------------------------------------------------------- /.github/workflows/pr-verify.yml: -------------------------------------------------------------------------------- 1 | name: pr-verify 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | pr-verify-job: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [macos-latest, ubuntu-latest] 11 | steps: 12 | - name: Check Out Code 13 | uses: actions/checkout@v4 14 | - name: Set Up NodeJS 15 | uses: actions/setup-node@v4 16 | with: 17 | node-version: '18' 18 | - name: Set Up Java 19 | uses: actions/setup-java@v4 20 | with: 21 | java-version: '21' 22 | distribution: 'adopt' 23 | - run: npm install -g typescript "vsce" 24 | - run: wget http://download.eclipse.org/jdtls/snapshots/jdt-language-server-latest.tar.gz 25 | - run: mkdir server && tar -xvzf jdt-language-server-latest.tar.gz -C ./server && rm jdt-language-server-latest.tar.gz 26 | - run: npm install 27 | - run: npm run repo:check 28 | - run: npm run compile 29 | - run: npm run vscode:prepublish 30 | - run: vsce package 31 | - run: ls -ll java-*vsix 32 | - run: npm run eslint 33 | - name: Run Tests 34 | if: runner.os == 'Linux' 35 | env: 36 | SKIP_COMMANDS_TEST: true 37 | run: $(echo "xvfb-run --auto-servernum") npm run test --silent 38 | - name: Run Tests 39 | if: runner.os != 'Linux' 40 | env: 41 | SKIP_COMMANDS_TEST: true 42 | SKIP_CLASSPATH_TEST: true 43 | run: npm run test --silent 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | server 3 | node_modules 4 | *.vsix 5 | .DS_Store 6 | .vscode-test 7 | undefined 8 | target 9 | dist 10 | jre 11 | lombok 12 | bin/ 13 | .settings 14 | .classpath 15 | .project 16 | test/resources/projects/**/.vscode 17 | test/resources/projects/maven/salut/testGradle 18 | test-temp 19 | -------------------------------------------------------------------------------- /.sdkmanrc: -------------------------------------------------------------------------------- 1 | # Enable auto-env through the sdkman_auto_env config 2 | # Add key=value pairs of SDKs to use below 3 | java=21.0.5-tem 4 | -------------------------------------------------------------------------------- /.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 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 6 | "dbaeumer.vscode-eslint", 7 | "EditorConfig.EditorConfig", 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 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "debugWebviews": true, 11 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 12 | "env": { 13 | "DEBUG_VSCODE_JAVA":"true", 14 | "VSCODE_REDHAT_TELEMETRY_DEBUG":"true" 15 | }, 16 | "stopOnEntry": false, 17 | "sourceMaps": true, 18 | "outFiles": [ "${workspaceRoot}/dist/**/*.js" ], 19 | "preLaunchTask": "npm: watch", 20 | "rendererDebugOptions": { 21 | "urlFilter": "*redhat.java*", 22 | "sourceMaps": true, 23 | } 24 | }, 25 | { 26 | "name": "Launch Extension - Remote Server", 27 | "type": "extensionHost", 28 | "request": "launch", 29 | "runtimeExecutable": "${execPath}", 30 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 31 | "stopOnEntry": false, 32 | "sourceMaps": true, 33 | "outFiles": [ "${workspaceRoot}/dist/**/*.js" ], 34 | "env": { 35 | "JDTLS_SERVER_PORT": "3333", 36 | "DEBUG_VSCODE_JAVA":"true", 37 | "VSCODE_REDHAT_TELEMETRY_DEBUG":"true" 38 | }, 39 | "preLaunchTask": "npm: watch" 40 | }, 41 | { 42 | "name": "Launch Extension - JDTLS Client", 43 | "type": "extensionHost", 44 | "request": "launch", 45 | "runtimeExecutable": "${execPath}", 46 | "debugWebviews": true, 47 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 48 | "stopOnEntry": false, 49 | "sourceMaps": true, 50 | "outFiles": [ "${workspaceRoot}/dist/**/*.js" ], 51 | "env": { 52 | "JDTLS_CLIENT_PORT": "5036", 53 | "DEBUG_VSCODE_JAVA":"true", 54 | "VSCODE_REDHAT_TELEMETRY_DEBUG":"true" 55 | }, 56 | "preLaunchTask": "npm: watch", 57 | "rendererDebugOptions": { 58 | "urlFilter": "*redhat.java*", 59 | "sourceMaps": true, 60 | } 61 | }, 62 | { 63 | "name": "Launch Extension - SyntaxLS Client", 64 | "type": "extensionHost", 65 | "request": "launch", 66 | "runtimeExecutable": "${execPath}", 67 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 68 | "stopOnEntry": false, 69 | "sourceMaps": true, 70 | "outFiles": [ "${workspaceRoot}/dist/**/*.js" ], 71 | "env": { 72 | "SYNTAXLS_CLIENT_PORT": "5037", 73 | "DEBUG_VSCODE_JAVA":"true", 74 | "VSCODE_REDHAT_TELEMETRY_DEBUG":"true" 75 | }, 76 | "preLaunchTask": "npm: watch" 77 | }, 78 | { 79 | "name": "Launch Extension - Hybrid Clients", 80 | "type": "extensionHost", 81 | "request": "launch", 82 | "runtimeExecutable": "${execPath}", 83 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 84 | "stopOnEntry": false, 85 | "sourceMaps": true, 86 | "outFiles": [ "${workspaceRoot}/dist/**/*.js" ], 87 | "env": { 88 | "JDTLS_CLIENT_PORT": "5036", 89 | "SYNTAXLS_CLIENT_PORT": "5037", 90 | "DEBUG_VSCODE_JAVA":"true", 91 | "VSCODE_REDHAT_TELEMETRY_DEBUG":"true" 92 | }, 93 | "preLaunchTask": "npm: watch" 94 | }, 95 | { 96 | "name": "Launch Tests - Standard Mode", 97 | "type": "extensionHost", 98 | "request": "launch", 99 | "runtimeExecutable": "${execPath}", 100 | "args": [ 101 | "${workspaceFolder}/test-temp/", 102 | "--disable-extensions", 103 | "--extensionDevelopmentPath=${workspaceRoot}", 104 | "--extensionTestsPath=${workspaceRoot}/out/test/standard-mode-suite" 105 | ], 106 | "stopOnEntry": false, 107 | "sourceMaps": true, 108 | "outFiles": ["${workspaceRoot}/out/**/*.js"], 109 | "preLaunchTask": "prepareStandardTest", 110 | "postDebugTask": "cleanTestFolder" 111 | }, 112 | { 113 | "name": "Launch Tests - Lightweight Mode", 114 | "type": "extensionHost", 115 | "request": "launch", 116 | "runtimeExecutable": "${execPath}", 117 | "args": [ 118 | "${workspaceFolder}/test-temp/", 119 | "--disable-extensions", 120 | "--extensionDevelopmentPath=${workspaceRoot}", 121 | "--extensionTestsPath=${workspaceRoot}/out/test/lightweight-mode-suite" 122 | ], 123 | "stopOnEntry": false, 124 | "sourceMaps": true, 125 | "outFiles": ["${workspaceRoot}/out/**/*.js"], 126 | "preLaunchTask": "prepareLightweightTest", 127 | "postDebugTask": "cleanTestFolder" 128 | }, 129 | { 130 | "args": [ 131 | "${input:gulpTask}" 132 | ], 133 | "name": "Launch Gulp Task", 134 | "program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js", 135 | "request": "launch", 136 | "skipFiles": [ 137 | "/**" 138 | ], 139 | "type": "node" 140 | } 141 | ], 142 | "inputs": [ 143 | { 144 | "id": "gulpTask", 145 | "type": "promptString", 146 | "description": "Name of the Gulp task to execute", 147 | "default": "download_jre" 148 | } 149 | ] 150 | } 151 | -------------------------------------------------------------------------------- /.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 | "typescript.tsdk": "./node_modules/typescript/lib", 10 | "git.alwaysSignOff": true, 11 | "vsicons.presets.angular": false // we want to use the TS server from our node_modules folder to control its version 12 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "npm: watch", 8 | "type": "npm", 9 | "script": "watch", 10 | "problemMatcher": ["$ts-webpack-watch", "$eslint-stylish"], 11 | "isBackground": true, 12 | "presentation": { 13 | "reveal": "never" 14 | }, 15 | "group": { 16 | "kind": "build", 17 | "isDefault": true 18 | } 19 | }, 20 | { 21 | "type": "gulp", 22 | "label": "cleanTestFolder", 23 | "task": "clean_test_folder" 24 | }, 25 | { 26 | "type": "gulp", 27 | "label": "generateStandardTestFolder", 28 | "task": "generate_standard_test_folder" 29 | }, 30 | { 31 | "type": "gulp", 32 | "label": "generateLightweightTestFolder", 33 | "task": "generate_lightweight_test_folder" 34 | }, 35 | { 36 | "label": "prepareStandardTest", 37 | "dependsOn": [ 38 | "cleanTestFolder", 39 | "generateStandardTestFolder", 40 | "npm: compile" 41 | ] 42 | }, 43 | { 44 | "label": "prepareLightweightTest", 45 | "dependsOn": [ 46 | "cleanTestFolder", 47 | "generateLightweightTestFolder", 48 | "npm: compile" 49 | ] 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | typings/** 3 | out/** 4 | test/** 5 | src/** 6 | images/** 7 | **/*.map 8 | .gitignore 9 | .github/** 10 | tsconfig.json 11 | tsconfig.webview.json 12 | tsconfig.base.json 13 | vsc-extension-quickstart.md 14 | undefined/** 15 | CONTRIBUTING.md 16 | .vscode-test/** 17 | **/*.vsix 18 | **/*.tar.gz 19 | webpack.*.json 20 | node_modules 21 | .editorconfig 22 | .travis.yml 23 | gulpfile.js 24 | Jenkinsfile 25 | .eslintrc.json 26 | .eslintignore 27 | webpack.config.js 28 | .DS_Store 29 | .github/** 30 | .gitignore 31 | .tool-versions 32 | .sdkmanrc -------------------------------------------------------------------------------- /DCO: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 1 Letterman Drive 6 | Suite D4700 7 | San Francisco, CA, 94129 8 | 9 | Everyone is permitted to copy and distribute verbatim copies of this 10 | license document, but changing it is not allowed. 11 | 12 | 13 | Developer's Certificate of Origin 1.1 14 | 15 | By making a contribution to this project, I certify that: 16 | 17 | (a) The contribution was created in whole or in part by me and I 18 | have the right to submit it under the open source license 19 | indicated in the file; or 20 | 21 | (b) The contribution is based upon previous work that, to the best 22 | of my knowledge, is covered under an appropriate open source 23 | license and I have the right under that license to submit that 24 | work with modifications, whether created in whole or in part 25 | by me, under the same open source license (unless I am 26 | permitted to submit under a different license), as indicated 27 | in the file; or 28 | 29 | (c) The contribution was provided directly to me by some other 30 | person who certified (a), (b) or (c) and I have not modified 31 | it. 32 | 33 | (d) I understand and agree that this project and the contribution 34 | are public and that a record of the contribution (including all 35 | personal information I submit with it, including my sign-off) is 36 | maintained indefinitely and may be redistributed consistent with 37 | this project or the open source license(s) involved. 38 | -------------------------------------------------------------------------------- /USAGE_DATA.md: -------------------------------------------------------------------------------- 1 | # Data collection 2 | 3 | vscode-java has opt-in telemetry collection, provided by [vscode-redhat-telemetry](https://github.com/redhat-developer/vscode-redhat-telemetry). 4 | 5 | ## What's included in the vscode-java telemetry data 6 | 7 | * vscode-java emits telemetry events when the extension starts and stops, 8 | which contain the common data mentioned on the 9 | [vscode-redhat-telemetry page](https://github.com/redhat-developer/vscode-redhat-telemetry/blob/main/USAGE_DATA.md#common-data). 10 | * The name of the build tool used to import a project (eg. Maven, Gradle, Invisible (project), etc.) 11 | * The total number of Java projects within the workspace 12 | * The lowest and highest Java compiler source level used (eg. 11 & 17) 13 | * Whether the project(s) are being imported for the first time (eg. true) 14 | * The elapsed time (in milliseconds) at which the language server initialized the workspace project(s), declared as ready for requests, and completed building the project(s) 15 | * The number of libraries that were indexed after project initialization 16 | * The total size (in bytes) of libraries that were indexed after project initialization 17 | * The number of error markers on the project(s) 18 | * The number of unresolved imports within the project(s) 19 | * Errors relating to running the language server, such as the message & stacktrace 20 | * Whether there is a mismatch between the project's requested source level, and the JDK used for the project (eg. true) 21 | * Information about the following settings. In the case of settings that store a well defined value (eg. path/url/string), we simply collect whether the setting has been set. 22 | * `java.settings.url`, `java.format.settings.url`, `java.quickfix.showAt`, `java.symbols.includeSourceMethodDeclarations`, `java.completion.collapseCompletionItems`, `java.completion.guessMethodArguments`, `java.completion.postfix.enabled`, `java.cleanup.actionsOnSave`, `java.sharedIndexes.enabled`, `java.inlayHints.parameterNames.enabled`, `java.server.launchMode`, `java.autobuild.enabled`, `java.jdt.ls.javac.enabled` 23 | * The extension name and the choice made when a recommendation to install a 3rd party extension is proposed 24 | * The name of Java commands being manually executed, and any resulting errors 25 | * The number of results (eg. 20), whether an error occurred (eg. false), engine type (eg. 'ecj', 'dom') and duration (in milliseconds) when code assist is activated 26 | * Whether the language server ran out of memory and the maximum allocated memory at which that occurred (eg. 200m) 27 | 28 | ## What's included in the general telemetry data 29 | 30 | Please see the 31 | [vscode-redhat-telemetry data collection information](https://github.com/redhat-developer/vscode-redhat-telemetry/blob/HEAD/USAGE_DATA.md#usage-data-being-collected-by-red-hat-extensions) 32 | for information on what data it collects. 33 | 34 | ## How to opt in or out 35 | 36 | Use the `redhat.telemetry.enabled` setting in order to enable or disable telemetry collection. 37 | 38 | This extension also abides by Visual Studio Code's telemetry level: if `telemetry.telemetryLevel` is set to `off`, then no telemetry events will be sent to Red Hat, even if `redhat.telemetry.enabled` is set to `true`. If `telemetry.telemetryLevel` is set to `error` or `crash`, only events containing an error or errors property will be sent to Red Hat. 39 | -------------------------------------------------------------------------------- /document/_java.metadataFilesGeneration.md: -------------------------------------------------------------------------------- 1 | # Metadata Files Generation 2 | 3 | We use the setting `java.import.generatesMetadataFilesAtProjectRoot` to control where the project metadata files(.project, .classpath, .factorypath, .settings/) will be generated: 4 | - `true`: Metadata files will be generated at the project's root. 5 | - `false`: Metadata files will be generated at the workspace storage. To be specific, the path will be: `/redhat.java/jdt_ws/.metadata/.plugins/org.eclipse.core.resources/.projects//`. 6 | 7 | By default, the setting is set to `false`. 8 | 9 | > If the metadata files exist in both the project root and the workspace storage, the extension will pick the files in the project root. 10 | 11 | ## Change the setting value 12 | 13 | Depending on how you change the setting, some extra steps need to be taken to make the change take effect. 14 | 15 | ### Change from `false` to `true` 16 | You need to restart the client. 17 | 18 | ### Change from `true` to `false` 19 | 1. Close the client. 20 | 2. Remove the metadata files in the project by your own. 21 | 3. Open the client. 22 | -------------------------------------------------------------------------------- /document/_java.notCoveredExecution.md: -------------------------------------------------------------------------------- 1 | # Not Covered Maven Plugin Execution 2 | 3 | ## Background 4 | Some Maven projects use 3rd party Maven plugins to generate sources or resources, and you may find that the generated code is not picked up by the project classpath, or the Maven goals are not executed by Java extension. This is a known technical issue with `m2e`, the underlying Maven integration tool for Java extension. The reason is that the Maven tooling `m2e` doesn't know if it's safe to run your Maven plugin automatically during a workspace build, so it does not run them by default and requires explicit instructions on how to handle them. Learn more about this issue from the wiki about [Execution Not Covered](https://www.eclipse.org/m2e/documentation/m2e-execution-not-covered.html). 5 | 6 | ## Workaround 7 | - Option 1: The best thing is still to request the Maven plugin authors to provide native integration with m2e. Here is a guideline on [how to make Maven plugins compatible with m2e](https://www.eclipse.org/m2e/documentation/m2e-making-maven-plugins-compat.html). 8 | 9 | - Option 2: Use [build-helper-maven-plugin](http://www.mojohaus.org/build-helper-maven-plugin/usage.html) to explicitly add the unrecognized source folder to classpath. 10 | 11 | ```xml 12 | 13 | ... 14 | 15 | 16 | 17 | org.codehaus.mojo 18 | build-helper-maven-plugin 19 | 3.2.0 20 | 21 | 22 | add-source 23 | generate-sources 24 | 25 | add-source 26 | 27 | 28 | 29 | some directory 30 | ... 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ``` 40 | 41 | - Option 3: Configure a lifecycle mapping metadata in pom.xml that explicitly tells m2e what to do with your plugin. 42 | 43 | For example, add [a processing instruction in the pom.xml](https://www.eclipse.org/m2e/documentation/release-notes-17.html#new-syntax-for-specifying-lifecycle-mapping-metadata) like `` to execute it on every project configuration update. 44 | 45 | You can use quick fixes to generate the inline lifecycle mapping in pom.xml, or manually configure it in pom.xml. If it's yourself that manually configure it, you have to let VS Code update the project configuration as well. The command is `"Java: Reload Projects"`. 46 | 47 | ```xml 48 | 49 | ... 50 | 51 | 52 | 53 | ro.isdc.wro4j 54 | wro4j-maven-plugin 55 | 1.8.0 56 | 57 | 58 | 59 | generate-resources 60 | 61 | run 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | ``` 70 | 71 | If you have multiple Maven plugins that need to configure the lifecycle mapping metadata, you can also configure them together in a dedicated `pluginManagement` section. 72 | 73 | ```xml 74 | 75 | ... 76 | 77 | 78 | 79 | 81 | 82 | org.eclipse.m2e 83 | lifecycle-mapping 84 | 1.0.0 85 | 86 | 87 | 88 | 89 | 90 | ro.isdc.wro4j 91 | wro4j-maven-plugin 92 | [1.8.0,) 93 | 94 | run 95 | 96 | 97 | 98 | 99 | true 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | ``` 112 | -------------------------------------------------------------------------------- /document/_java.templateVariables.md: -------------------------------------------------------------------------------- 1 | # Predefined Variables for Java Template Snippets 2 | 3 | Below are the predefined variables you could use in the template settings such as `java.templates.fileHeader` and `java.templates.typeComment`. 4 | - `${file_name}` - name of the current Java file 5 | - `${package_name}` - name of the enclosing package 6 | - `${type_name}` - name of the current type 7 | - `${user}` - current user system login name 8 | - `${date}` - current system date 9 | - `${time}` - current system time 10 | - `${year}` - current year 11 | - `${month}` - current month 12 | - `${shortmonth}` - short form representation of current month 13 | - `${day}` - current day of the month 14 | - `${hour}` - current hour 15 | - `${minute}` - current minute 16 | -------------------------------------------------------------------------------- /document/refactoring_change_signature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/document/refactoring_change_signature.png -------------------------------------------------------------------------------- /document/refactoring_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/document/refactoring_menu.png -------------------------------------------------------------------------------- /icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/icons/icon128.png -------------------------------------------------------------------------------- /images/changelog/ChooseProduct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/ChooseProduct.png -------------------------------------------------------------------------------- /images/changelog/ClientPort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/ClientPort.png -------------------------------------------------------------------------------- /images/changelog/CreateNewConfiguration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/CreateNewConfiguration.png -------------------------------------------------------------------------------- /images/changelog/DebugConfigurationOpen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/DebugConfigurationOpen.png -------------------------------------------------------------------------------- /images/changelog/DebugRemoteServer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/DebugRemoteServer.png -------------------------------------------------------------------------------- /images/changelog/RemoteServer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/RemoteServer.png -------------------------------------------------------------------------------- /images/changelog/RemoteServerInVSCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/RemoteServerInVSCode.png -------------------------------------------------------------------------------- /images/changelog/SocketSteamInVSCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/SocketSteamInVSCode.png -------------------------------------------------------------------------------- /images/changelog/importMavenProject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/importMavenProject.png -------------------------------------------------------------------------------- /images/changelog/importProject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/importProject.png -------------------------------------------------------------------------------- /images/changelog/importedMavenProject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/importedMavenProject.png -------------------------------------------------------------------------------- /images/changelog/loadingTargetPlatform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/loadingTargetPlatform.png -------------------------------------------------------------------------------- /images/changelog/reloadTargetPlatform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/reloadTargetPlatform.png -------------------------------------------------------------------------------- /images/changelog/setTargetPlatform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/changelog/setTargetPlatform.png -------------------------------------------------------------------------------- /images/statusMarker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/statusMarker.png -------------------------------------------------------------------------------- /images/vscode-java.0.0.1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/images/vscode-java.0.0.1.gif -------------------------------------------------------------------------------- /language-support/html/inline-html.json: -------------------------------------------------------------------------------- 1 | { 2 | "injectionSelector": "L:source.java -comment -string", 3 | "patterns": [ 4 | { 5 | "contentName": "meta.embedded.block.html", 6 | "begin": "(?i)((/\\*\\s*(language=html)\\s*\\*/)|((//\\s*(language=html)\\s*)))", 7 | "beginCaptures": { 8 | "1": { 9 | "name": "comment.block" 10 | } 11 | }, 12 | "end": "(?<=\")", 13 | "patterns": [ 14 | { 15 | "begin": "\\s*(\"\"\")$", 16 | "beginCaptures": { 17 | "0": { "name": "string.quoted.triple.java" } 18 | }, 19 | "end": "\\s*(\"\"\")", 20 | "endCaptures": { 21 | "0": { "name": "string.quoted.triple.java" } 22 | }, 23 | "patterns": [ 24 | { "include": "text.html.derivative" } 25 | ] 26 | }, 27 | { 28 | "begin": "\\s*(\")", 29 | "beginCaptures": { 30 | "0": { "name": "string.quoted.double.java" } 31 | }, 32 | "end": "\\s*(\")", 33 | "endCaptures": { 34 | "0": { "name": "string.quoted.double.java" } 35 | }, 36 | "patterns": [ 37 | { "include": "text.html.derivative" } 38 | ] 39 | } 40 | ] 41 | } 42 | ], 43 | "scopeName": "inline.html" 44 | } 45 | -------------------------------------------------------------------------------- /language-support/java/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "//", 4 | "blockComment": [ 5 | "/*", 6 | "*/" 7 | ] 8 | }, 9 | "brackets": [ 10 | [ 11 | "{", 12 | "}" 13 | ], 14 | [ 15 | "[", 16 | "]" 17 | ], 18 | [ 19 | "(", 20 | ")" 21 | ], 22 | [ 23 | "\"\"\"", 24 | "\"\"\"" 25 | ] 26 | ], 27 | "autoClosingPairs": [ 28 | [ 29 | "{", 30 | "}" 31 | ], 32 | [ 33 | "[", 34 | "]" 35 | ], 36 | [ 37 | "(", 38 | ")" 39 | ], 40 | { 41 | "open": "\"", 42 | "close": "\"", 43 | "notIn": [ 44 | "string" 45 | ] 46 | }, 47 | [ 48 | "'", 49 | "'" 50 | ], 51 | { 52 | "open": "/*", 53 | "close": " */", 54 | "notIn": [ 55 | "string" 56 | ] 57 | }, 58 | { 59 | "open": "\"\"\"", 60 | "close": "\"\"\";", 61 | "notIn": [ 62 | "comment" 63 | ] 64 | } 65 | ], 66 | "surroundingPairs": [ 67 | [ 68 | "{", 69 | "}" 70 | ], 71 | [ 72 | "[", 73 | "]" 74 | ], 75 | [ 76 | "(", 77 | ")" 78 | ], 79 | [ 80 | "\"", 81 | "\"" 82 | ], 83 | [ 84 | "'", 85 | "'" 86 | ], 87 | [ 88 | "<", 89 | ">" 90 | ] 91 | ] 92 | } -------------------------------------------------------------------------------- /language-support/json/inline-json.json: -------------------------------------------------------------------------------- 1 | { 2 | "injectionSelector": "L:source.java -comment -string", 3 | "patterns": [ 4 | { 5 | "contentName": "meta.embedded.block.json", 6 | "begin": "(?i)((/\\*\\s*(language=json)\\s*\\*/)|((//\\s*(language=json)\\s*)))", 7 | "beginCaptures": { 8 | "1": { 9 | "name": "comment.block" 10 | } 11 | }, 12 | "end": "(?<=\")", 13 | "patterns": [ 14 | { 15 | "begin": "\\s*(\"\"\")$", 16 | "beginCaptures": { 17 | "0": { "name": "string.quoted.triple.java" } 18 | }, 19 | "end": "\\s*(\"\"\")", 20 | "endCaptures": { 21 | "0": { "name": "string.quoted.triple.java" } 22 | }, 23 | "patterns": [ 24 | { "include": "source.json" } 25 | ] 26 | }, 27 | { 28 | "begin": "\\s*(\")", 29 | "beginCaptures": { 30 | "0": { "name": "string.quoted.double.java" } 31 | }, 32 | "end": "\\s*(\")", 33 | "endCaptures": { 34 | "0": { "name": "string.quoted.double.java" } 35 | }, 36 | "patterns": [ 37 | { "include": "source.json" } 38 | ] 39 | } 40 | ] 41 | } 42 | ], 43 | "scopeName": "inline.json" 44 | } 45 | -------------------------------------------------------------------------------- /language-support/kotlin/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "//", 4 | "blockComment": [ "/*", "*/" ] 5 | }, 6 | "brackets": [ 7 | ["{", "}"], 8 | ["[", "]"], 9 | ["(", ")"] 10 | ], 11 | "autoClosingPairs": [ 12 | { "open": "{", "close": "}" }, 13 | { "open": "[", "close": "]" }, 14 | { "open": "(", "close": ")" }, 15 | { "open": "'", "close": "'", "notIn": ["string", "comment"] }, 16 | { "open": "\"", "close": "\"", "notIn": ["string"] }, 17 | { "open": "/*", "close": " */", "notIn": ["string"] } 18 | ], 19 | "surroundingPairs": [ 20 | ["{", "}"], 21 | ["[", "]"], 22 | ["(", ")"], 23 | ["<", ">"], 24 | ["'", "'"], 25 | ["\"", "\""] 26 | ] 27 | } -------------------------------------------------------------------------------- /language-support/properties/JavaProperties.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "information_for_contributors": [ 3 | "This file has been converted from https://github.com/textmate/java.tmbundle/blob/master/Syntaxes/JavaProperties.plist", 4 | "If you want to provide a fix or improvement, please create a pull request against the original repository.", 5 | "Once accepted there, we are happy to receive an update request." 6 | ], 7 | "version": "https://github.com/textmate/java.tmbundle/blob/79b8b61865e40de0ff04bb16fe1076b154b9894c/Syntaxes/JavaProperties.plist", 8 | "fileTypes": [ 9 | "properties" 10 | ], 11 | "foldingStartMarker": "^[a-zA-Z0-9.-_]+=.*\\\r\n", 12 | "foldingStopMarker": "^(.*(?" 65 | ] 66 | ] 67 | } -------------------------------------------------------------------------------- /language-support/sql/inline-sql.json: -------------------------------------------------------------------------------- 1 | { 2 | "injectionSelector": "L:source.java -comment -string", 3 | "patterns": [ 4 | { 5 | "contentName": "meta.embedded.block.sql", 6 | "begin": "(?i)((/\\*\\s*(language=sql)\\s*\\*/)|((//\\s*(language=sql)\\s*)))", 7 | "beginCaptures": { 8 | "1": { 9 | "name": "comment.block" 10 | } 11 | }, 12 | "end": "(?<=\")", 13 | "patterns": [ 14 | { 15 | "begin": "\\s*(\"\"\")$", 16 | "beginCaptures": { 17 | "0": { "name": "string.quoted.triple.java" } 18 | }, 19 | "end": "\\s*(\"\"\")", 20 | "endCaptures": { 21 | "0": { "name": "string.quoted.triple.java" } 22 | }, 23 | "patterns": [ 24 | { "include": "source.sql" }, 25 | { "include": "source.plpgsql.postgres" } 26 | ] 27 | }, 28 | { 29 | "begin": "\\s*(\")", 30 | "beginCaptures": { 31 | "0": { "name": "string.quoted.double.java" } 32 | }, 33 | "end": "\\s*(\")", 34 | "endCaptures": { 35 | "0": { "name": "string.quoted.double.java" } 36 | }, 37 | "patterns": [ 38 | { "include": "source.sql" }, 39 | { "include": "source.plpgsql.postgres" } 40 | ] 41 | } 42 | ] 43 | } 44 | ], 45 | "scopeName": "inline.sql" 46 | } 47 | -------------------------------------------------------------------------------- /language-support/xml/inline-xml.json: -------------------------------------------------------------------------------- 1 | { 2 | "injectionSelector": "L:source.java -comment -string", 3 | "patterns": [ 4 | { 5 | "contentName": "meta.embedded.block.xml", 6 | "begin": "(?i)((/\\*\\s*(language=xml)\\s*\\*/)|((//\\s*(language=xml)\\s*)))", 7 | "beginCaptures": { 8 | "1": { 9 | "name": "comment.block" 10 | } 11 | }, 12 | "end": "(?<=\")", 13 | "patterns": [ 14 | { 15 | "begin": "\\s*(\"\"\")$", 16 | "beginCaptures": { 17 | "0": { "name": "string.quoted.triple.java" } 18 | }, 19 | "end": "\\s*(\"\"\")", 20 | "endCaptures": { 21 | "0": { "name": "string.quoted.triple.java" } 22 | }, 23 | "patterns": [ 24 | { "include": "text.xml" } 25 | ] 26 | }, 27 | { 28 | "begin": "\\s*(\")", 29 | "beginCaptures": { 30 | "0": { "name": "string.quoted.double.java" } 31 | }, 32 | "end": "\\s*(\")", 33 | "endCaptures": { 34 | "0": { "name": "string.quoted.double.java" } 35 | }, 36 | "patterns": [ 37 | { "include": "text.xml" } 38 | ] 39 | } 40 | ] 41 | } 42 | ], 43 | "scopeName": "inline.xml" 44 | } 45 | -------------------------------------------------------------------------------- /language-support/yaml/inline-yaml.json: -------------------------------------------------------------------------------- 1 | { 2 | "injectionSelector": "L:source.java -comment -string", 3 | "patterns": [ 4 | { 5 | "contentName": "meta.embedded.block.yaml", 6 | "begin": "(?i)((/\\*\\s*(language=yaml)\\s*\\*/)|((//\\s*(language=yaml)\\s*)))", 7 | "beginCaptures": { 8 | "1": { 9 | "name": "comment.block" 10 | } 11 | }, 12 | "end": "(?<=\")", 13 | "patterns": [ 14 | { 15 | "begin": "\\s*(\"\"\")$", 16 | "beginCaptures": { 17 | "0": { "name": "string.quoted.triple.java" } 18 | }, 19 | "end": "\\s*(\"\"\")", 20 | "endCaptures": { 21 | "0": { "name": "string.quoted.triple.java" } 22 | }, 23 | "patterns": [ 24 | { "include": "source.yaml" } 25 | ] 26 | }, 27 | { 28 | "begin": "\\s*(\")", 29 | "beginCaptures": { 30 | "0": { "name": "string.quoted.double.java" } 31 | }, 32 | "end": "\\s*(\")", 33 | "endCaptures": { 34 | "0": { "name": "string.quoted.double.java" } 35 | }, 36 | "patterns": [ 37 | { "include": "source.yaml" } 38 | ] 39 | } 40 | ] 41 | } 42 | ], 43 | "scopeName": "inline.yaml" 44 | } 45 | -------------------------------------------------------------------------------- /package.nls.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.server.mode.switch": "Switch to Standard Mode", 3 | "java.projectConfiguration.update": "Reload Projects", 4 | "java.project.import": "Import Java Projects into Workspace", 5 | "java.open.serverLog": "Open Java Language Server Log File", 6 | "java.open.serverStdoutLog": "Open Java Language Server Output Log File", 7 | "java.open.serverStderrLog": "Open Java Language Server Error Log File", 8 | "java.open.clientLog": "Open Java Extension Log File", 9 | "java.open.logs": "Open All Log Files", 10 | "java.workspace.compile": "Force Java Compilation", 11 | "java.project.build": "Rebuild Projects", 12 | "java.open.formatter.settings": "Open Java Formatter Settings", 13 | "java.clean.workspace": "Clean Java Language Server Workspace", 14 | "java.project.updateSourceAttachment": "Attach Source...", 15 | "java.project.addToSourcePath": "Add Folder to Java Source Path", 16 | "java.project.removeFromSourcePath": "Remove Folder from Java Source Path", 17 | "java.project.listSourcePaths": "List All Java Source Paths", 18 | "java.show.server.task.status": "Show Build Job Status", 19 | "java.action.navigateToSuperImplementation": "Go to Super Implementation", 20 | "java.action.showTypeHierarchy": "Show Type Hierarchy", 21 | "java.action.showClassHierarchy": "Show Class Hierarchy", 22 | "java.action.showSupertypeHierarchy": "Show Supertype Hierarchy", 23 | "java.action.showSubtypeHierarchy": "Show Subtype Hierarchy", 24 | "java.action.changeBaseType": "Base on this Type", 25 | "java.project.createModuleInfo.command": "Create module-info.java", 26 | "java.clean.sharedIndexes": "Clean Shared Indexes", 27 | "java.server.restart": "Restart Java Language Server", 28 | "java.edit.smartSemicolonDetection": "Java Smart Semicolon Detection", 29 | "java.action.filesExplorerPasteAction": "Paste Clipboard Text Into a File", 30 | "java.action.doCleanup": "Performs Cleanup Actions", 31 | "java.change.searchScope": "Change Search Scope", 32 | "java.action.showExtendedOutline": "Open Extended Outline" 33 | } 34 | -------------------------------------------------------------------------------- /package.nls.ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.server.mode.switch": "Standard 모드로 전환", 3 | "java.projectConfiguration.update": "프로젝트 다시 로드", 4 | "java.project.import": "작업 영역으로 Java 프로젝트 가져오기", 5 | "java.open.serverLog": "Java Language Server 로그 파일 열기", 6 | "java.open.serverStdoutLog": "Java Language Server 출력 로그 파일 열기", 7 | "java.open.serverStderrLog": "Java Language Server 오류 로그 파일 열기", 8 | "java.open.clientLog": "Java Extension 로그 파일 열기", 9 | "java.open.logs": "모든 로그 파일 열기", 10 | "java.workspace.compile": "Java 강제 컴파일", 11 | "java.project.build": "프로젝트 다시 빌드", 12 | "java.open.formatter.settings": "Java Formatter 설정 열기", 13 | "java.clean.workspace": "Java Language Server 작업 영역 정리", 14 | "java.project.updateSourceAttachment": "Attach Source...", 15 | "java.project.addToSourcePath": "Java Source 경로에 폴더 추가", 16 | "java.project.removeFromSourcePath": "Java Source 경로에서 폴더 제거", 17 | "java.project.listSourcePaths": "모든 Java Source 경로를 나열", 18 | "java.show.server.task.status": "빌드 작업 상태 표시", 19 | "java.action.navigateToSuperImplementation": "상위 구현으로 이동", 20 | "java.action.showTypeHierarchy": "Type 계층 구조 표시", 21 | "java.action.showClassHierarchy": "Class 계층 구조 표시", 22 | "java.action.showSupertypeHierarchy": "Supertype 계층 구조 표시", 23 | "java.action.showSubtypeHierarchy": "Subtype 계층 구조 표시", 24 | "java.action.changeBaseType": "이 유형을 기준으로", 25 | "java.project.createModuleInfo.command": "module-info.java 생성", 26 | "java.action.filesExplorerPasteAction": "클립보드 텍스트를 파일에 붙여넣기", 27 | "java.action.doCleanup": "정리 작업을 수행합니다" 28 | } 29 | -------------------------------------------------------------------------------- /package.nls.zh-cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.server.mode.switch": "切换到标准模式", 3 | "java.projectConfiguration.update": "重新加载项目", 4 | "java.project.import": "将 Java 项目导入工作区", 5 | "java.open.serverLog": "打开 Java 语言服务器日志文件", 6 | "java.open.serverStdoutLog": "打开 Java 语言服务器输出日志文件", 7 | "java.open.serverStderrLog": "打开 Java 语言服务器错误日志文件", 8 | "java.open.clientLog": "打开 Java 插件日志文件", 9 | "java.open.logs": "打开所有日志文件", 10 | "java.workspace.compile": "强制编译", 11 | "java.project.build": "重新构建项目", 12 | "java.open.formatter.settings": "打开 Java 格式设置", 13 | "java.clean.workspace": "清理 Java 语言服务器工作区", 14 | "java.project.updateSourceAttachment": "附加源代码...", 15 | "java.project.addToSourcePath": "将文件夹加入 Java 源代码路径", 16 | "java.project.removeFromSourcePath": "将文件夹从 Java 源代码路径中删除", 17 | "java.project.listSourcePaths": "列出所有 Java 源代码路径", 18 | "java.show.server.task.status": "显示工作状态", 19 | "java.action.navigateToSuperImplementation": "转到父类实现", 20 | "java.action.showTypeHierarchy": "显示类型层次结构", 21 | "java.action.showClassHierarchy": "显示类的继承关系", 22 | "java.action.showSupertypeHierarchy": "显示父类层次结构", 23 | "java.action.showSubtypeHierarchy": "显示子类层次结构", 24 | "java.action.changeBaseType": "基于此类型", 25 | "java.project.createModuleInfo.command": "创建 module-info.java", 26 | "java.clean.sharedIndexes": "清理共享的索引文件", 27 | "java.action.filesExplorerPasteAction": "将剪贴板文本粘贴到文件中", 28 | "java.action.doCleanup": "执行清理操作" 29 | } 30 | -------------------------------------------------------------------------------- /package.nls.zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.server.mode.switch": "切換到標準模式", 3 | "java.projectConfiguration.update": "重新載入專案", 4 | "java.project.import": "匯入 Java 專案到工作區", 5 | "java.open.serverLog": "開啟 Java 語言伺服器日誌檔", 6 | "java.open.serverStdoutLog": "開啟 Java 語言伺服器輸出日誌檔", 7 | "java.open.serverStderrLog": "開啟 Java 語言伺服器錯誤日誌檔", 8 | "java.open.clientLog": "開啟 Java 延伸模組日誌檔", 9 | "java.open.logs": "開啟所有日誌檔", 10 | "java.workspace.compile": "強制編譯 Java", 11 | "java.project.build": "重新建置專案", 12 | "java.open.formatter.settings": "開啟 Java 格式設定", 13 | "java.clean.workspace": "清理 Java 語言伺服器工作區", 14 | "java.project.updateSourceAttachment": "附加原始碼...", 15 | "java.project.addToSourcePath": "將資料夾加入 Java 原始碼路徑", 16 | "java.project.removeFromSourcePath": "從 Java 始碼路徑中移除資料夾", 17 | "java.project.listSourcePaths": "列出所有 Java 始碼路徑", 18 | "java.show.server.task.status": "顯示建置工作狀態", 19 | "java.action.navigateToSuperImplementation": "移到父類別中的實作", 20 | "java.action.showTypeHierarchy": "顯示型別階層結構", 21 | "java.action.showClassHierarchy": "顯示類別階層結構", 22 | "java.action.showSupertypeHierarchy": "顯示父類別階層結構", 23 | "java.action.showSubtypeHierarchy": "顯示子類別階層結構", 24 | "java.action.changeBaseType": "以此型別為基礎", 25 | "java.project.createModuleInfo.command": "創建 module-info.java", 26 | "java.action.filesExplorerPasteAction": "將剪貼簿文字貼到文件中", 27 | "java.action.doCleanup": "執行清理操作" 28 | } -------------------------------------------------------------------------------- /schemas/package.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "Java Language server contributions to package.json", 4 | "type": "object", 5 | "properties": { 6 | "contributes": { 7 | "type": "object", 8 | "properties": { 9 | "javaExtensions": { 10 | "type": "array", 11 | "markdownDescription": "Java language server extensions", 12 | "items": { 13 | "type": "string", 14 | "description": "Relative path to a Java language server extension JAR file" 15 | } 16 | }, 17 | "javaBuildFilePatterns": { 18 | "type": "array", 19 | "markdownDescription": "Java build file patterns", 20 | "items": { 21 | "type": "string", 22 | "description": "Regular expressions for specifying build file" 23 | } 24 | }, 25 | "javaBuildTools": { 26 | "type": "array", 27 | "description": "Information about the cared build files. Will be used when 'java.import.projectSelection' is 'manual'.", 28 | "items": { 29 | "type": "object", 30 | "properties": { 31 | "displayName": { 32 | "description": "The display name of the build file type.", 33 | "type": "string" 34 | }, 35 | "buildFileNames": { 36 | "description": "The build file names that supported by the build tool.", 37 | "type": "array", 38 | "items": { 39 | "type": "string" 40 | } 41 | } 42 | } 43 | } 44 | }, 45 | "javaShortcuts": { 46 | "type": "array", 47 | "description": "Shortcuts to be listed when clicking server status bar item.", 48 | "items": { 49 | "type": "object", 50 | "properties": { 51 | "title": { 52 | "description": "The title of the quick pick item.", 53 | "type": "string" 54 | }, 55 | "command": { 56 | "description": "The command to be executed when the quick pick is selected.", 57 | "type": "string" 58 | }, 59 | "arguments": { 60 | "description": "The arguments to be passed to the command.", 61 | "type": "array" 62 | } 63 | } 64 | } 65 | } 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /snippets/server.json: -------------------------------------------------------------------------------- 1 | { 2 | "sysout": { 3 | "prefix": ["sysout", "sout", "System.out.println()"], 4 | "body": [ 5 | "System.out.println($0);" 6 | ], 7 | "description": "Print to standard out" 8 | }, 9 | "syserr": { 10 | "prefix": ["syserr", "serr", "System.err.println()"], 11 | "body": [ 12 | "System.err.println($0);" 13 | ], 14 | "description": "Print to standard err" 15 | }, 16 | "fori": { 17 | "prefix": ["fori"], 18 | "body": [ 19 | "for (${1:int} ${2:i} = ${3:0}; ${2:i} < ${4:max}; ${2:i}++) {", 20 | "\t$0", 21 | "}" 22 | ], 23 | "description": "Indexed for loop" 24 | }, 25 | "foreach": { 26 | "prefix": ["foreach", "iter"], 27 | "body": [ 28 | "for (${1:type} ${2:var} : ${3:iterable}) {", 29 | "\t$0", 30 | "}" 31 | ], 32 | "description": "Enhanced for loop" 33 | }, 34 | "if": { 35 | "prefix": ["if"], 36 | "body": [ 37 | "if (${1:condition}) {", 38 | "\t$0", 39 | "}" 40 | ], 41 | "description": "if statement" 42 | }, 43 | "ifelse": { 44 | "prefix": ["ifelse"], 45 | "body": [ 46 | "if (${1:condition}) {", 47 | "\t$2", 48 | "} else {", 49 | "\t$0", 50 | "}" 51 | ], 52 | "description": "if/else statement" 53 | }, 54 | "ifnull": { 55 | "prefix": ["ifnull"], 56 | "body": [ 57 | "if (${1:condition} == null) {", 58 | "\t$0", 59 | "}" 60 | ], 61 | "description": "if statement checking for null" 62 | }, 63 | "ifnotnull": { 64 | "prefix": ["ifnotnull"], 65 | "body": [ 66 | "if (${1:condition} != null) {", 67 | "\t$0", 68 | "}" 69 | ], 70 | "description": "if statement checking for not null" 71 | }, 72 | "While Statement": { 73 | "prefix": ["while"], 74 | "body": [ 75 | "while (${1:condition}) {", 76 | "\t$0", 77 | "}" 78 | ], 79 | "description": "While Statement" 80 | }, 81 | "Do-While Statement": { 82 | "prefix": ["dowhile"], 83 | "body": [ 84 | "do {", 85 | "\t$0", 86 | "} while (${1:condition});" 87 | ], 88 | "description": "Do-While Statement" 89 | }, 90 | "Switch Statement": { 91 | "prefix": "switch", 92 | "body": [ 93 | "switch (${1:key}) {", 94 | "\tcase ${2:value}:", 95 | "\t\t$0", 96 | "\t\tbreak;", 97 | "", 98 | "\tdefault:", 99 | "\t\tbreak;", 100 | "}" 101 | ], 102 | "description": "Switch Statement" 103 | }, 104 | "trycatch": { 105 | "prefix": "try_catch", 106 | "body": [ 107 | "try {", 108 | "\t${TM_SELECTED_TEXT:$1}", 109 | "} catch (${2:Exception} ${3:e}) {", 110 | "\t$0// TODO: handle exception", 111 | "}" 112 | ], 113 | "description": "try/catch block" 114 | }, 115 | "tryresources": { 116 | "prefix": "try_resources", 117 | "body": [ 118 | "try ($1) {", 119 | "\t$2", 120 | "} catch (${3:Exception} ${4:e}) {", 121 | "\t$0// TODO: handle exception", 122 | "}" 123 | ] 124 | }, 125 | "main": { 126 | "prefix": ["main", "psvm", "public static void main(String[] args)"], 127 | "body": [ 128 | "public static void main(String[] args) {", 129 | "\t$0", 130 | "}" 131 | ], 132 | "description": "Public static main method" 133 | }, 134 | "Constructor": { 135 | "prefix": "ctor", 136 | "body": [ 137 | "${1|public,protected,private|} ${2:${TM_FILENAME_BASE}}($3) {", 138 | "\t${4:super();}$0", 139 | "}" 140 | ], 141 | "description": "Constructor" 142 | }, 143 | "method": { 144 | "prefix": "method", 145 | "body": [ 146 | "${1|public,protected,private|}${2| , static |}${3:void} ${4:name}($5) {", 147 | "\t$0", 148 | "}" 149 | ], 150 | "description": "Method" 151 | }, 152 | "newObject": { 153 | "prefix": "new", 154 | "body": [ 155 | "${1:Object} ${2:foo} = new ${1}($3);", 156 | "$0" 157 | ], 158 | "description": "Create new Object" 159 | }, 160 | "Field": { 161 | "prefix": "field", 162 | "body": [ 163 | "${1|public,protected,private|} ${2:String} ${3:name};" 164 | ], 165 | "description": "Field" 166 | } 167 | } -------------------------------------------------------------------------------- /src/buildpath.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { window, commands, ExtensionContext, Uri, ConfigurationTarget } from 'vscode'; 4 | import { Commands } from './commands'; 5 | import { getJavaConfiguration } from './utils'; 6 | 7 | interface Result { 8 | status: boolean; 9 | message: string; 10 | } 11 | 12 | interface SourcePath { 13 | path: string; 14 | displayPath: string; 15 | projectName: string; 16 | projectType: string; 17 | } 18 | 19 | export interface ListCommandResult extends Result { 20 | data?: SourcePath[]; 21 | } 22 | 23 | export function registerCommands(context: ExtensionContext) { 24 | context.subscriptions.push(commands.registerCommand(Commands.ADD_TO_SOURCEPATH_CMD, async (uri: Uri) => { 25 | const result = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.ADD_TO_SOURCEPATH, uri.toString()); 26 | if (result.status) { 27 | if (result.sourcePaths) { 28 | getJavaConfiguration().update('project.sourcePaths', result.sourcePaths, ConfigurationTarget.Workspace); 29 | } 30 | window.showInformationMessage(result.message ? result.message : 'Successfully added the folder to the source path.'); 31 | } else { 32 | window.showErrorMessage(result.message); 33 | } 34 | })); 35 | 36 | context.subscriptions.push(commands.registerCommand(Commands.REMOVE_FROM_SOURCEPATH_CMD, async (uri: Uri) => { 37 | const result = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.REMOVE_FROM_SOURCEPATH, uri.toString()); 38 | if (result.status) { 39 | if (result.sourcePaths) { 40 | getJavaConfiguration().update('project.sourcePaths', result.sourcePaths, ConfigurationTarget.Workspace); 41 | } 42 | window.showInformationMessage(result.message ? result.message : 'Successfully removed the folder from the source path.'); 43 | } else { 44 | window.showErrorMessage(result.message); 45 | } 46 | })); 47 | 48 | context.subscriptions.push(commands.registerCommand(Commands.LIST_SOURCEPATHS_CMD, async() => { 49 | const result: ListCommandResult = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.LIST_SOURCEPATHS); 50 | if (result.status) { 51 | if (!result.data || !result.data.length) { 52 | window.showInformationMessage("No Java source directories found in the workspace, please use the command 'Add Folder to Java Source Path' first."); 53 | } else { 54 | window.showQuickPick(result.data.map(sourcePath => { 55 | return { 56 | label: sourcePath.displayPath, 57 | detail: `$(file-directory) ${sourcePath.projectType} Project: ${sourcePath.projectName}`, 58 | }; 59 | }), { placeHolder: 'All Java source directories recognized by the workspace.'}); 60 | } 61 | } else { 62 | window.showErrorMessage(result.message); 63 | } 64 | })); 65 | } 66 | -------------------------------------------------------------------------------- /src/clientCodeActionProvider.ts: -------------------------------------------------------------------------------- 1 | import { CancellationToken, CodeAction, CodeActionContext, CodeActionKind, CodeActionProvider, Command, ExtensionContext, ProviderResult, Range, Selection, TextDocument, commands } from "vscode"; 2 | import { apiManager } from "./apiManager"; 3 | 4 | const configureStaticImportsCommand = "java.action.configureFavoriteStaticMembers"; 5 | const UNDEFINED_METHOD = "67108964"; 6 | export class ClientCodeActionProvider implements CodeActionProvider { 7 | constructor(readonly context: ExtensionContext) { 8 | context.subscriptions.push(commands.registerCommand(configureStaticImportsCommand, async () => { 9 | commands.executeCommand("workbench.action.openSettings", "java.completion.favoriteStaticMembers"); 10 | apiManager.fireTraceEvent({ 11 | name: "java.ls.command", 12 | properties: { 13 | command: configureStaticImportsCommand, 14 | }, 15 | }); 16 | })); 17 | } 18 | 19 | provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult<(CodeAction | Command)[]> { 20 | const codeActions = []; 21 | if (context.diagnostics?.some(diagnostic => diagnostic.code === UNDEFINED_METHOD) 22 | || document.lineAt(range.start.line)?.text?.startsWith("import ")) { 23 | const action = new CodeAction("Configure static import...", CodeActionKind.QuickFix); 24 | action.command = { 25 | title: "Configure static import...", 26 | command: configureStaticImportsCommand, 27 | }; 28 | codeActions.push(action); 29 | } 30 | return codeActions; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/clientErrorHandler.ts: -------------------------------------------------------------------------------- 1 | import { window, commands } from "vscode"; 2 | import { serverStatusBarProvider } from './serverStatusBarProvider'; 3 | import { ErrorHandler, Message, ErrorAction, CloseAction, ErrorHandlerResult, CloseHandlerResult } from "vscode-languageclient"; 4 | import { Commands } from "./commands"; 5 | import { logger } from "./log"; 6 | import { apiManager } from "./apiManager"; 7 | 8 | const CLIENT_ERROR = "java.client.error"; 9 | export class ClientErrorHandler implements ErrorHandler { 10 | private restarts: number[]; 11 | 12 | constructor(private name: string) { 13 | this.restarts = []; 14 | } 15 | 16 | public error(_error: Error, _message: Message, count: number): ErrorHandlerResult { 17 | if (count && count <= 3) { 18 | logger.error(`${this.name} server encountered error: ${_message}, ${_error && _error.toString()}`); 19 | return { 20 | action: ErrorAction.Continue, 21 | handled: true 22 | }; 23 | } 24 | 25 | const errorMessage = `${this.name} server encountered error and will shut down: ${_message}, ${_error && _error.toString()}`; 26 | apiManager.fireTraceEvent({ 27 | name: CLIENT_ERROR, 28 | properties: { 29 | message: errorMessage, 30 | }, 31 | }); 32 | logger.error(errorMessage); 33 | return { 34 | action: ErrorAction.Shutdown, 35 | handled: true 36 | }; 37 | } 38 | 39 | public closed(): CloseHandlerResult { 40 | this.restarts.push(Date.now()); 41 | if (this.restarts.length < 5) { 42 | logger.error(`The ${this.name} server crashed and will restart.`); 43 | return { 44 | action: CloseAction.Restart, 45 | handled: true 46 | }; 47 | } else { 48 | const diff = this.restarts[this.restarts.length - 1] - this.restarts[0]; 49 | if (diff <= 3 * 60 * 1000) { 50 | const message = `The ${this.name} server crashed 5 times in the last 3 minutes. The server will not be restarted.`; 51 | apiManager.fireTraceEvent({ 52 | name: CLIENT_ERROR, 53 | properties: { 54 | message, 55 | }, 56 | }); 57 | logger.error(message); 58 | serverStatusBarProvider.setError(); 59 | const action = "Show logs"; 60 | window.showErrorMessage(message, action).then(selection => { 61 | if (selection === action) { 62 | commands.executeCommand(Commands.OPEN_LOGS); 63 | } 64 | }); 65 | return { 66 | action: CloseAction.DoNotRestart, 67 | handled: true 68 | }; 69 | } 70 | 71 | logger.error(`The ${this.name} server crashed and will restart.`); 72 | this.restarts.shift(); 73 | return { 74 | action: CloseAction.Restart, 75 | handled: true 76 | }; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/codeActionProvider.ts: -------------------------------------------------------------------------------- 1 | import { CodeActionProvider, CodeActionProviderMetadata, CodeActionKind } from "vscode"; 2 | import { Commands } from "./commands"; 3 | 4 | /** 5 | * Mapping the refactoring kind to its section id in the document 6 | */ 7 | export const javaRefactorKinds: Map = new Map([ 8 | [CodeActionKind.Refactor, 'java-refactoring'], 9 | [CodeActionKind.RefactorExtract, 'extract-to-constant'], 10 | [CodeActionKind.RefactorExtract.append('function'), 'extract-to-method'], 11 | [CodeActionKind.RefactorExtract.append('constant'), 'extract-to-constant'], 12 | [CodeActionKind.RefactorExtract.append('variable'), 'extract-to-local-variable'], 13 | [CodeActionKind.RefactorExtract.append('field'), 'extract-to-field'], 14 | [CodeActionKind.RefactorInline, 'inline-constant'], 15 | [CodeActionKind.Refactor.append('move'), 'move'], 16 | [CodeActionKind.Refactor.append('assign'), 'assign-to-variable'], 17 | [CodeActionKind.Refactor.append('introduce').append('parameter'), 'introduce-parameter'] 18 | ]); 19 | 20 | export class RefactorDocumentProvider implements CodeActionProvider { 21 | provideCodeActions() { 22 | return [{ 23 | // The aim of this is to expose the source actions in the light bulb. 24 | title: "Source Actions...", 25 | command: "editor.action.sourceAction", 26 | kind: CodeActionKind.Empty, 27 | }]; 28 | } 29 | 30 | public static readonly metadata: CodeActionProviderMetadata = { 31 | providedCodeActionKinds: [ 32 | CodeActionKind.Refactor 33 | ], 34 | documentation: Array.from(javaRefactorKinds.keys()).map(kind => { 35 | return { 36 | kind, 37 | command: { 38 | command: Commands.LEARN_MORE_ABOUT_REFACTORING, 39 | title: 'Learn more about Java refactorings...', 40 | arguments: [kind] 41 | } 42 | }; 43 | }), 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /src/diagnostic.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext, window } from "vscode"; 2 | import { ValidateDocumentNotification } from "./protocol"; 3 | import { LanguageClient } from "vscode-languageclient/node"; 4 | import { validateAllOpenBuffersOnChanges } from "./settings"; 5 | 6 | export function registerDocumentValidationListener(context: ExtensionContext, languageClient: LanguageClient) { 7 | context.subscriptions.push(window.onDidChangeActiveTextEditor(textEditor => { 8 | // Refresh the diagnostics when the focus is switched to a Java file. 9 | if (textEditor?.document.uri?.scheme === "file" && textEditor?.document.languageId === "java") { 10 | if (!validateAllOpenBuffersOnChanges()) { 11 | languageClient.sendNotification(ValidateDocumentNotification.type, { 12 | textDocument: { 13 | uri: textEditor.document.uri.toString(), 14 | }, 15 | }); 16 | } 17 | } 18 | })); 19 | } 20 | -------------------------------------------------------------------------------- /src/documentSymbols.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { 4 | CancellationToken, 5 | DocumentSymbol, 6 | DocumentSymbolParams, 7 | DocumentSymbolRequest, 8 | SymbolInformation 9 | } from "vscode-languageclient"; 10 | import { LanguageClient } from "vscode-languageclient/node"; 11 | import { getActiveLanguageClient } from "./extension"; 12 | 13 | type DocumentSymbolsResponse = DocumentSymbol[] | SymbolInformation[] | null; 14 | 15 | export type GetDocumentSymbolsCommand = (params: DocumentSymbolParams, token?: CancellationToken) => Promise; 16 | 17 | export function getDocumentSymbolsProvider(): GetDocumentSymbolsCommand { 18 | return async (params: DocumentSymbolParams, token?: CancellationToken): Promise => { 19 | const languageClient: LanguageClient | undefined = await getActiveLanguageClient(); 20 | if (!languageClient) { 21 | return []; 22 | } 23 | 24 | if (token !== undefined) { 25 | return languageClient.sendRequest(DocumentSymbolRequest.type, params, token); 26 | } 27 | return languageClient.sendRequest(DocumentSymbolRequest.type, params); 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/errorUtils.ts: -------------------------------------------------------------------------------- 1 | // copied from https://github.com/redhat-developer/openshift-dd-ext/blob/f8c053bded9bc6c1bfac682cf4867b187e87ee76/client/src/utils/ErrorUtils.ts#L12 2 | export function getMessage(error: any): string { 3 | return getRawMessage(error).trim(); 4 | } 5 | 6 | export function getRawMessage(error: any): string { 7 | if (typeof error === 'string') { 8 | return error; 9 | } 10 | if (error.stderr) { 11 | return error.stderr; 12 | } 13 | if (error.message) { 14 | return error.message; 15 | } 16 | if (error.error) { 17 | return error.error; 18 | } 19 | 20 | // Unlikely to happen, as we're either getting an Error object with a message 21 | // or we're dealing with a failed promise with a stderr message 22 | // in any other case, we'll need to figure out what to return on a case by case basis 23 | return JSON.stringify(error); 24 | } -------------------------------------------------------------------------------- /src/goToDefinition.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { 4 | CancellationToken, DefinitionParams, 5 | DefinitionRequest, Location, 6 | LocationLink 7 | } from 'vscode-languageclient'; 8 | import { LanguageClient } from 'vscode-languageclient/node'; 9 | import { getActiveLanguageClient } from './extension'; 10 | 11 | type GoToDefinitionResponse = Location | Location[] | LocationLink[] | null; 12 | 13 | export type GoToDefinitionCommand = (params: DefinitionParams, token?: CancellationToken) => Promise; 14 | 15 | export function goToDefinitionProvider(): GoToDefinitionCommand { 16 | return async (params: DefinitionParams, token?: CancellationToken): Promise => { 17 | const languageClient: LanguageClient | undefined = await getActiveLanguageClient(); 18 | if (!languageClient) { 19 | return null; 20 | } 21 | 22 | if (token !== undefined) { 23 | return languageClient.sendRequest(DefinitionRequest.type, params, token); 24 | } 25 | return languageClient.sendRequest(DefinitionRequest.type, params); 26 | }; 27 | } -------------------------------------------------------------------------------- /src/gradle/gradleCodeActionProvider.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as fse from "fs-extra"; 4 | import * as path from "path"; 5 | import { CancellationToken, CodeAction, CodeActionContext, CodeActionKind, CodeActionProvider, CodeActionProviderMetadata, Command, commands, Diagnostic, DiagnosticRelatedInformation, ExtensionContext, ProviderResult, Range, Selection, TextDocument, Uri } from "vscode"; 6 | import { Commands } from "../commands"; 7 | import { upgradeGradle } from "../standardLanguageClientUtils"; 8 | 9 | const UPGRADE_GRADLE_WRAPPER_TITLE = "Upgrade Gradle Wrapper"; 10 | const WRAPPER_PROPERTIES_DESCRIPTOR = "gradle/wrapper/gradle-wrapper.properties"; 11 | const GRADLE_PROBLEM_ID = 0x00080000; 12 | const GRADLE_INVALID_TYPE_CODE_ID = GRADLE_PROBLEM_ID + 1; 13 | 14 | export class GradleCodeActionProvider implements CodeActionProvider { 15 | 16 | public provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult<(CodeAction | Command)[]> { 17 | if (context?.diagnostics?.length && context.diagnostics[0].source === "Java") { 18 | return this.provideGradleCodeActions(document, context.diagnostics); 19 | } 20 | return []; 21 | } 22 | 23 | async provideGradleCodeActions(document: TextDocument, diagnostics: readonly Diagnostic[]): Promise { 24 | const codeActions = []; 25 | for (const diagnostic of diagnostics) { 26 | if (diagnostic.message?.startsWith("The build file has been changed")) { 27 | const reloadProjectAction = new CodeAction("Reload project", CodeActionKind.QuickFix); 28 | reloadProjectAction.command = { 29 | title: "Reload Project", 30 | command: Commands.CONFIGURATION_UPDATE, 31 | arguments: [document.uri], 32 | }; 33 | codeActions.push(reloadProjectAction); 34 | continue; 35 | } 36 | 37 | const documentUri = document.uri.toString(); 38 | if (documentUri.endsWith(WRAPPER_PROPERTIES_DESCRIPTOR) && diagnostic.code === GRADLE_INVALID_TYPE_CODE_ID.toString()) { 39 | const projectPath = path.resolve(Uri.parse(documentUri).fsPath, "..", "..", "..").normalize(); 40 | if (await fse.pathExists(projectPath)) { 41 | const projectUri = Uri.file(projectPath).toString(); 42 | const upgradeWrapperCommand: Command = { 43 | title: UPGRADE_GRADLE_WRAPPER_TITLE, 44 | command: Commands.UPGRADE_GRADLE_WRAPPER_CMD, 45 | arguments: [projectUri] 46 | }; 47 | const codeAction = new CodeAction(UPGRADE_GRADLE_WRAPPER_TITLE, CodeActionKind.QuickFix.append("gradle")); 48 | codeAction.command = upgradeWrapperCommand; 49 | codeActions.push(codeAction); 50 | } 51 | } 52 | } 53 | return codeActions; 54 | } 55 | } 56 | 57 | export const gradleCodeActionMetadata: CodeActionProviderMetadata = { 58 | providedCodeActionKinds: [ 59 | CodeActionKind.QuickFix.append("gradle") 60 | ] 61 | }; 62 | -------------------------------------------------------------------------------- /src/hoverAction.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { HoverProvider, CancellationToken, Hover, Position, TextDocument, MarkdownString, MarkedString, Command } from "vscode"; 4 | import { TextDocumentPositionParams, HoverRequest } from "vscode-languageclient"; 5 | import { LanguageClient } from 'vscode-languageclient/node'; 6 | import { Commands as javaCommands } from "./commands"; 7 | import { FindLinks } from "./protocol"; 8 | import { ProvideHoverCommandFn } from "./extension.api"; 9 | import { logger } from "./log"; 10 | 11 | export function createClientHoverProvider(languageClient: LanguageClient): JavaHoverProvider { 12 | const hoverProvider: JavaHoverProvider = new JavaHoverProvider(languageClient); 13 | registerHoverCommand(async (params: TextDocumentPositionParams, token: CancellationToken) => { 14 | return await provideHoverCommand(languageClient, params, token); 15 | }); 16 | 17 | return hoverProvider; 18 | } 19 | 20 | async function provideHoverCommand(languageClient: LanguageClient, params: TextDocumentPositionParams, token: CancellationToken): Promise { 21 | const response = await languageClient.sendRequest(FindLinks.type, { 22 | type: 'superImplementation', 23 | position: params, 24 | }, token); 25 | if (response && response.length) { 26 | const location = response[0]; 27 | let tooltip; 28 | if (location.kind === 'method') { 29 | tooltip = `Go to super method '${location.displayName}'`; 30 | } else { 31 | tooltip = `Go to super implementation '${location.displayName}'`; 32 | } 33 | 34 | return [{ 35 | title: 'Go to Super Implementation', 36 | command: javaCommands.NAVIGATE_TO_SUPER_IMPLEMENTATION_COMMAND, 37 | tooltip, 38 | arguments: [{ 39 | uri: encodeBase64(location.uri), 40 | range: location.range, 41 | }], 42 | }]; 43 | } 44 | } 45 | 46 | function encodeBase64(text: string): string { 47 | return Buffer.from(text).toString('base64'); 48 | } 49 | 50 | const hoverCommandRegistry: ProvideHoverCommandFn[] = []; 51 | export function registerHoverCommand(callback: ProvideHoverCommandFn): void { 52 | hoverCommandRegistry.push(callback); 53 | } 54 | 55 | class JavaHoverProvider implements HoverProvider { 56 | 57 | constructor(readonly languageClient: LanguageClient) { 58 | } 59 | 60 | async provideHover(document: TextDocument, position: Position, token: CancellationToken): Promise { 61 | const params = { 62 | textDocument: this.languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document), 63 | position: this.languageClient.code2ProtocolConverter.asPosition(position), 64 | }; 65 | 66 | // Fetch the javadoc from Java language server. 67 | const hoverResponse = await this.languageClient.sendRequest(HoverRequest.type, params, token); 68 | const serverHover = this.languageClient.protocol2CodeConverter.asHover(hoverResponse); 69 | 70 | // Fetch the contributed hover commands from third party extensions. 71 | const contributedCommands: Command[] = await this.getContributedHoverCommands(params, token); 72 | if (!contributedCommands.length) { 73 | return serverHover; 74 | } 75 | 76 | const contributed = new MarkdownString(contributedCommands.map((command) => this.convertCommandToMarkdown(command)).join(' | ')); 77 | contributed.isTrusted = true; 78 | let contents: MarkdownString[] = [ contributed ]; 79 | let range; 80 | if (serverHover && serverHover.contents) { 81 | contents = contents.concat(serverHover.contents as MarkdownString[]); 82 | range = serverHover.range; 83 | } 84 | return new Hover(contents, range); 85 | } 86 | 87 | private async getContributedHoverCommands(params: TextDocumentPositionParams, token: CancellationToken): Promise { 88 | const contributedCommands: Command[] = []; 89 | for (const provideFn of hoverCommandRegistry) { 90 | try { 91 | if (token.isCancellationRequested) { 92 | break; 93 | } 94 | 95 | const commands = (await provideFn(params, token)) || []; 96 | commands.forEach((command) => { 97 | contributedCommands.push(command); 98 | }); 99 | } catch (error) { 100 | logger.error(`Failed to provide hover command ${String(error)}`); 101 | } 102 | } 103 | 104 | return contributedCommands; 105 | } 106 | 107 | private convertCommandToMarkdown(command: Command): string { 108 | return `[${command.title}](command:${command.command}?${encodeURIComponent(JSON.stringify(command.arguments || []))} "${command.tooltip || command.command}")`; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/javaClassEditor.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import * as vscode from 'vscode'; 3 | import { Uri, window, ExtensionContext} from "vscode"; 4 | import { getNonce } from "./webviewUtils"; 5 | 6 | class JavaClassDocument implements vscode.CustomDocument { 7 | constructor(uri: Uri) { this.uri = uri; } 8 | uri: Uri; 9 | dispose(): void { } 10 | } 11 | 12 | export class JavaClassEditorProvider implements vscode.CustomReadonlyEditorProvider { 13 | 14 | private context: ExtensionContext; 15 | 16 | openCustomDocument(uri: Uri, openContext: vscode.CustomDocumentOpenContext, token: vscode.CancellationToken): JavaClassDocument { 17 | return new JavaClassDocument(uri); 18 | } 19 | 20 | constructor (context: ExtensionContext) { 21 | this.context = context; 22 | } 23 | 24 | public static readonly viewType = 'decompiled.javaClass'; 25 | 26 | async resolveCustomEditor(document: vscode.CustomDocument, webviewPanel: vscode.WebviewPanel, token: vscode.CancellationToken): Promise { 27 | const nonce: string = getNonce(); 28 | webviewPanel.webview.options = { 29 | enableScripts: true, 30 | localResourceRoots: [Uri.joinPath(Uri.parse(this.context.extensionPath), 'webview-resources')] 31 | }; 32 | const classUri = Uri.parse((document.uri.toString()).replace(/^file/, "class")); 33 | const styleUri = Uri.file( 34 | path.join(this.context.extensionPath, 'webview-resources', 'button.css') 35 | ); 36 | const style: string = ``; 37 | webviewPanel.webview.html = ` 38 | 39 | 40 | 41 | ${style} 42 | 43 | 44 | 48 | 49 | 50 | `; 51 | webviewPanel.webview.onDidReceiveMessage(message => { 52 | switch (message.command) { 53 | case 'decompiled': 54 | webviewPanel.dispose(); 55 | window.showTextDocument(classUri, { preview: false }); 56 | return; 57 | } 58 | }, undefined, this.context.subscriptions); 59 | } 60 | } -------------------------------------------------------------------------------- /src/jdkUtils.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { existsSync } from 'fs'; 4 | import { IJavaRuntime, findRuntimes, getSources } from 'jdk-utils'; 5 | import { join } from 'path'; 6 | import { ExtensionContext, Uri, workspace } from 'vscode'; 7 | 8 | let cachedJdks: IJavaRuntime[]; 9 | let cachedJreNames: string[]; 10 | 11 | export async function loadSupportedJreNames(context: ExtensionContext): Promise { 12 | const config = await getContributesConfiguration(context, "java.configuration.runtimes"); 13 | cachedJreNames = config?.items?.properties?.name?.enum; 14 | } 15 | 16 | async function getContributesConfiguration(context: ExtensionContext, configId: string): Promise { 17 | const buffer = await workspace.fs.readFile(Uri.file(context.asAbsolutePath("package.json"))); 18 | const packageJson = JSON.parse(buffer.toString()); 19 | /** 20 | * contributes.configuration can either be a single object, 21 | * representing a single category of settings, or an array 22 | * of objects, representing multiple categories of settings. 23 | */ 24 | const categories = packageJson?.contributes?.configuration; 25 | if (Array.isArray(categories)) { 26 | for (const category of categories) { 27 | if (category?.properties?.[configId]) { 28 | return category.properties[configId]; 29 | } 30 | } 31 | } else { 32 | return categories?.properties?.[configId]; 33 | } 34 | } 35 | 36 | export function getSupportedJreNames(): string[] { 37 | return cachedJreNames; 38 | } 39 | 40 | export async function listJdks(force?: boolean): Promise { 41 | if (force || !cachedJdks) { 42 | cachedJdks = await findRuntimes({ checkJavac: true, withVersion: true, withTags: true }) 43 | .then(jdks => jdks.filter(jdk => { 44 | // Validate if it's a real Java Home. 45 | return existsSync(join(jdk.homedir, "lib", "rt.jar")) 46 | || existsSync(join(jdk.homedir, "jre", "lib", "rt.jar")) // Java 8 47 | || existsSync(join(jdk.homedir, "lib", "jrt-fs.jar")); // Java 9+ 48 | })); 49 | } 50 | 51 | return [].concat(cachedJdks); 52 | } 53 | 54 | /** 55 | * Sort by source where JDk is located. 56 | * The order is: 57 | * 1. JDK_HOME, JAVA_HOME, PATH 58 | * 2. JDK manager such as SDKMAN, jEnv, jabba, asdf 59 | * 3. Common places such as /usr/lib/jvm 60 | * 4. Others 61 | */ 62 | export function sortJdksBySource(jdks: IJavaRuntime[]) { 63 | const rankedJdks = jdks as Array; 64 | const env: string[] = ["JDK_HOME", "JAVA_HOME", "PATH"]; 65 | const jdkManagers: string[] = ["SDKMAN", "jEnv", "jabba", "asdf"]; 66 | for (const jdk of rankedJdks) { 67 | const detectedSources: string[] = getSources(jdk); 68 | for (const [index, source] of env.entries()) { 69 | if (detectedSources.includes(source)) { 70 | jdk.rank = index; // jdk from environment variables 71 | break; 72 | } 73 | } 74 | 75 | if (jdk.rank) { 76 | continue; 77 | } 78 | 79 | const fromManager: boolean = detectedSources.some(source => jdkManagers.includes(source)); 80 | if (fromManager) { 81 | jdk.rank = env.length + 1; // jdk from the jdk managers such as SDKMAN 82 | } else if (!detectedSources.length){ 83 | jdk.rank = env.length + 2; // jdk from common places 84 | } else { 85 | jdk.rank = env.length + 3; // jdk from other source such as ~/.gradle/jdks 86 | } 87 | } 88 | rankedJdks.sort((a, b) => a.rank - b.rank); 89 | } 90 | 91 | /** 92 | * Sort by major version in descend order. 93 | */ 94 | export function sortJdksByVersion(jdks: IJavaRuntime[]) { 95 | jdks.sort((a, b) => (b.version?.major ?? 0) - (a.version?.major ?? 0)); 96 | } 97 | -------------------------------------------------------------------------------- /src/languageStatusItemFactory.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as path from "path"; 4 | import * as vscode from "vscode"; 5 | import { Commands } from "./commands"; 6 | 7 | const languageServerDocumentSelector = [ 8 | { scheme: 'file', language: 'java' }, 9 | { scheme: 'jdt', language: 'java' }, 10 | { scheme: 'untitled', language: 'java' }, 11 | { pattern: '**/pom.xml' }, 12 | { pattern: '**/{build,settings}.gradle'}, 13 | { pattern: '**/{build,settings}.gradle.kts'} 14 | ]; 15 | 16 | export namespace StatusCommands { 17 | export const switchToStandardCommand = { 18 | title: "Load Projects", 19 | command: Commands.SWITCH_SERVER_MODE, 20 | arguments: ['Standard', true], 21 | tooltip: "LightWeight mode only provides limited features, please load projects to get full feature set" 22 | }; 23 | 24 | export const configureJavaRuntimeCommand = { 25 | title: "Configure Java Runtime", 26 | command: "workbench.action.openSettings", 27 | arguments: ["java.configuration.runtimes"], 28 | tooltip: "Configure Java Runtime" 29 | }; 30 | 31 | export const startStandardServerCommand = { 32 | title: "Select Projects...", 33 | command: Commands.SWITCH_SERVER_MODE, 34 | arguments: ['Standard', true], 35 | tooltip: "Select Projects..." 36 | }; 37 | } 38 | 39 | export namespace RuntimeStatusItemFactory { 40 | export function create(text: string, vmInstallPath: string): vscode.LanguageStatusItem { 41 | const item = vscode.languages.createLanguageStatusItem("javaRuntimeStatusItem", languageServerDocumentSelector); 42 | item.severity = vscode.LanguageStatusSeverity?.Information; 43 | item.name = "Java Runtime"; 44 | item.text = text; 45 | item.command = StatusCommands.configureJavaRuntimeCommand; 46 | if (vmInstallPath) { 47 | item.command.tooltip = `Language Level: ${text} <${vmInstallPath}>`; 48 | } 49 | return item; 50 | } 51 | 52 | export function update(item: any, text: string, vmInstallPath: string): void { 53 | item.text = text; 54 | item.command.tooltip = vmInstallPath ? `Language Level: ${text} <${vmInstallPath}>` : "Configure Java Runtime"; 55 | } 56 | } 57 | 58 | export namespace BuildFileStatusItemFactory { 59 | export function create(buildFilePath: string): vscode.LanguageStatusItem { 60 | const fileName = path.basename(buildFilePath); 61 | const item = vscode.languages.createLanguageStatusItem("javaBuildFileStatusItem", languageServerDocumentSelector); 62 | item.severity = vscode.LanguageStatusSeverity?.Information; 63 | item.name = "Java Build File"; 64 | item.text = fileName; 65 | item.command = getOpenBuildFileCommand(buildFilePath); 66 | return item; 67 | } 68 | 69 | export function update(item: any, buildFilePath: string): void { 70 | const fileName = path.basename(buildFilePath); 71 | item.text = fileName; 72 | item.command = getOpenBuildFileCommand(buildFilePath); 73 | } 74 | 75 | function getOpenBuildFileCommand(buildFilePath: string): vscode.Command { 76 | const relativePath = vscode.workspace.asRelativePath(buildFilePath); 77 | return { 78 | title: `Open Config File`, 79 | command: Commands.OPEN_BROWSER, 80 | arguments: [vscode.Uri.file(buildFilePath)], 81 | tooltip: `Open ${relativePath}` 82 | }; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/log.ts: -------------------------------------------------------------------------------- 1 | import { createLogger, format, transports } from 'winston'; 2 | import * as DailyRotateFile from 'winston-daily-rotate-file'; 3 | 4 | export function initializeLogFile(filename: string) { 5 | logger.add(new DailyRotateFile({ 6 | filename: filename, 7 | datePattern: 'YYYY-MM-DD', 8 | maxSize: '1m', // 1MB max size per file 9 | maxFiles: '2d' // retain logs of the last two days 10 | })); 11 | } 12 | 13 | export const logger = createLogger({ 14 | format: format.combine( 15 | format.timestamp({ 16 | format: 'YYYY-MM-DD HH:mm:ss.SSS' 17 | }), 18 | format.prettyPrint() 19 | ), 20 | transports: [ 21 | // See https://github.com/microsoft/vscode/issues/117327 22 | // Disable console.log for lsp trace because intense logging will freeze the code render process. 23 | // new transports.Console() 24 | ] 25 | }); 26 | -------------------------------------------------------------------------------- /src/markdownPreviewProvider.ts: -------------------------------------------------------------------------------- 1 | import { Disposable, WebviewPanel, window, ViewColumn, commands, Uri, Webview, ExtensionContext, env } from "vscode"; 2 | import * as fse from 'fs-extra'; 3 | import * as path from 'path'; 4 | import { Commands } from "./commands"; 5 | import { getNonce } from "./webviewUtils"; 6 | 7 | class MarkdownPreviewProvider implements Disposable { 8 | private panel: WebviewPanel | undefined; 9 | // a cache maps document path to rendered html 10 | private documentCache: Map = new Map(); 11 | private disposables: Disposable[] = []; 12 | 13 | public async show(markdownFilePath: string, title: string, section: string, context: ExtensionContext): Promise { 14 | if (!this.panel) { 15 | this.panel = window.createWebviewPanel('java.markdownPreview', title, ViewColumn.Active, { 16 | localResourceRoots: [ 17 | Uri.file(path.join(context.extensionPath, 'webview-resources')), 18 | Uri.file(path.dirname(markdownFilePath)), 19 | ], 20 | retainContextWhenHidden: true, 21 | enableFindWidget: true, 22 | enableScripts: true, 23 | }); 24 | } 25 | 26 | this.disposables.push(this.panel.onDidDispose(() => { 27 | this.panel = undefined; 28 | })); 29 | 30 | this.panel.iconPath = Uri.file(path.join(context.extensionPath, 'icons', 'icon128.png')); 31 | this.panel.webview.html = await this.getHtmlContent(this.panel.webview, markdownFilePath, section, context); 32 | this.panel.title = title; 33 | this.panel.reveal(this.panel.viewColumn); 34 | } 35 | 36 | public dispose(): void { 37 | if (this.panel) { 38 | this.panel.dispose(); 39 | } 40 | for (const disposable of this.disposables) { 41 | disposable.dispose(); 42 | } 43 | } 44 | 45 | protected async getHtmlContent(webview: Webview, markdownFilePath: string, section: string, context: ExtensionContext): Promise { 46 | const nonce: string = getNonce(); 47 | const styles: string = this.getStyles(webview, context); 48 | let body: string | undefined = this.documentCache.get(markdownFilePath); 49 | if (!body) { 50 | let markdownString: string = await fse.readFile(markdownFilePath, 'utf8'); 51 | markdownString = markdownString.replace(/__VSCODE_ENV_APPNAME_PLACEHOLDER__/, env.appName); 52 | body = await commands.executeCommand(Commands.MARKDOWN_API_RENDER, markdownString); 53 | this.documentCache.set(markdownFilePath, body); 54 | } 55 | return ` 56 | 57 | 58 | 59 | 60 | 61 | 62 | ${styles} 63 | 64 | 65 | 66 | ${body} 67 | 72 | 84 | 85 | 86 | `; 87 | } 88 | 89 | protected getStyles(webview: Webview, context: ExtensionContext): string { 90 | const styles: Uri[] = [ 91 | Uri.file(path.join(context.extensionPath, 'webview-resources', 'highlight.css')), 92 | Uri.file(path.join(context.extensionPath, 'webview-resources', 'markdown.css')), 93 | Uri.file(path.join(context.extensionPath, 'webview-resources', 'document.css')), 94 | ]; 95 | return styles.map((styleUri: Uri) => ``).join('\n'); 96 | } 97 | } 98 | 99 | export const markdownPreviewProvider: MarkdownPreviewProvider = new MarkdownPreviewProvider(); 100 | -------------------------------------------------------------------------------- /src/outline/extendedOutlineQuickPick.ts: -------------------------------------------------------------------------------- 1 | import { DocumentSymbolParams, LanguageClient, TextDocumentIdentifier } from "vscode-languageclient/node"; 2 | import { getActiveLanguageClient } from "../extension"; 3 | import { ExtendedDocumentSymbolRequest } from "./protocol"; 4 | import { Location, Position, QuickPick, QuickPickItem, Uri, window, workspace } from "vscode"; 5 | import { getLThemeIcon } from "../themeUtils"; 6 | 7 | export class ExtendedOutlineQuickPick { 8 | private api: QuickPick; 9 | private client: LanguageClient; 10 | public initialized: boolean; 11 | 12 | constructor() { 13 | this.initialized = false; 14 | } 15 | 16 | async initialize() { 17 | this.api = window.createQuickPick(); 18 | this.api.ignoreFocusOut = true; 19 | this.api.onDidChangeActive((items: QuickPickItem[]) => { 20 | if (items.length > 0) { 21 | const active: QuickPickItem = items[0]; 22 | const uri = active["uri"]; 23 | const range = active["range"]; 24 | if (uri !== undefined) { 25 | workspace.openTextDocument(Uri.parse(uri)).then(doc => { 26 | window.showTextDocument(doc, {preserveFocus: true, selection: range}); 27 | }); 28 | } else { 29 | window.showTextDocument(window.activeTextEditor.document, {preserveFocus: true, selection: range}); 30 | } 31 | } 32 | }); 33 | this.api.onDidAccept(() => { 34 | this.api.hide(); 35 | }); 36 | this.client = await getActiveLanguageClient(); 37 | this.initialized = true; 38 | } 39 | 40 | async open(uri: Uri) { 41 | if (!this.initialized) { 42 | await this.initialize(); 43 | } 44 | 45 | if (!this.api) { 46 | return; 47 | } 48 | 49 | const location = new Location(uri, new Position(0, 0)); 50 | const params: DocumentSymbolParams = { 51 | textDocument: TextDocumentIdentifier.create(location.uri.toString()) 52 | }; 53 | const symbols = await this.client.sendRequest(ExtendedDocumentSymbolRequest.type, params); 54 | let quickPickItems: QuickPickItem[] = []; 55 | for (const s of symbols) { 56 | const icon = getLThemeIcon(s.kind).id; 57 | const item = { 58 | label: `$(${icon}) ${s.name}`, 59 | description: s.detail.trim(), 60 | uri: s.uri, 61 | range: s.range 62 | }; 63 | quickPickItems.push(item); 64 | if (icon === 'symbol-class' || icon === 'symbol-interface') { 65 | const items: QuickPickItem[] = s.children.map(s => ({ 66 | label: `$(${getLThemeIcon(s.kind).id}) ${s.name}`, 67 | // custom quick pick has automatic space between label & description 68 | description: s.detail.trim(), 69 | uri: s.uri, 70 | range: s.range 71 | })); 72 | quickPickItems = quickPickItems.concat(items); 73 | } 74 | } 75 | this.api.items = quickPickItems; 76 | this.api.activeItems = []; 77 | this.api.show(); 78 | } 79 | } 80 | 81 | export const extendedOutlineQuickPick: ExtendedOutlineQuickPick = new ExtendedOutlineQuickPick(); 82 | -------------------------------------------------------------------------------- /src/outline/protocol.ts: -------------------------------------------------------------------------------- 1 | import { DocumentSymbol, DocumentSymbolParams, RequestType } from "vscode-languageclient"; 2 | 3 | export namespace ExtendedDocumentSymbolRequest { 4 | export const type = new RequestType('java/extendedDocumentSymbol'); 5 | } 6 | 7 | export interface ExtendedDocumentSymbol extends DocumentSymbol { 8 | uri: string; 9 | children?: ExtendedDocumentSymbol[]; 10 | } -------------------------------------------------------------------------------- /src/outputInfoCollector.ts: -------------------------------------------------------------------------------- 1 | import { OutputChannel, window, ViewColumn } from "vscode"; 2 | import { logger } from "./log"; 3 | 4 | export class OutputInfoCollector implements OutputChannel { 5 | private channel: OutputChannel = null; 6 | 7 | constructor(public name: string) { 8 | this.channel = window.createOutputChannel(this.name); 9 | } 10 | 11 | append(value: string): void { 12 | logger.info(value); 13 | this.channel.append(value); 14 | } 15 | 16 | appendLine(value: string): void { 17 | logger.info(value); 18 | this.channel.appendLine(value); 19 | } 20 | 21 | replace(value: string): void { 22 | this.clear(); 23 | this.append(value); 24 | } 25 | 26 | clear(): void { 27 | this.channel.clear(); 28 | } 29 | 30 | show(preserveFocus?: boolean): void; 31 | show(column?: ViewColumn, preserveFocus?: boolean): void; 32 | show(column?: any, preserveFocus?: any) { 33 | this.channel.show(column, preserveFocus); 34 | } 35 | 36 | hide(): void { 37 | this.channel.hide(); 38 | } 39 | 40 | dispose(): void { 41 | this.channel.dispose(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/pasteAction.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { TextEncoder } from 'util'; 4 | import { commands, env, ExtensionContext, Range, TextEditor, Uri, window, workspace } from 'vscode'; 5 | import { LanguageClient } from 'vscode-languageclient/node'; 6 | import { apiManager } from './apiManager'; 7 | import { Commands } from './commands'; 8 | import fs = require('fs'); 9 | 10 | export function registerCommands(languageClient: LanguageClient, context: ExtensionContext) { 11 | context.subscriptions.push(commands.registerCommand(Commands.CLIPBOARD_ONPASTE, () => { 12 | registerOrganizeImportsOnPasteCommand(); 13 | })); 14 | } 15 | 16 | export async function registerOrganizeImportsOnPasteCommand(): Promise { 17 | const clipboardText: string = await env.clipboard.readText(); 18 | const editor: TextEditor = window.activeTextEditor; 19 | const documentText: string = editor.document.getText(); 20 | const numCursors = editor.selections.length; 21 | let bits: string[] = []; 22 | if (numCursors > 1) { 23 | bits = clipboardText.split(/\r?\n/); 24 | } 25 | const action = editor.edit(textInserter => { 26 | for (let i = 0; i < numCursors; i++) { 27 | const selection = editor.selections[i]; 28 | const isCursorOnly = selection.isEmpty; 29 | const text = bits.length === numCursors ? bits[i] : clipboardText; 30 | if (isCursorOnly) { 31 | textInserter.insert(selection.start, text); 32 | } 33 | else { 34 | const start = selection.start; 35 | const end = selection.end; 36 | textInserter.replace(new Range(start, end), text); 37 | } 38 | } 39 | }); 40 | 41 | action.then((wasApplied) => { 42 | if (wasApplied && editor.document.languageId === "java") { 43 | const fileURI = editor.document.uri.toString(); 44 | const hasText: boolean = documentText !== null && /\S/.test(documentText); 45 | if (hasText) { 46 | // Organize imports silently to avoid surprising the user 47 | commands.executeCommand(Commands.ORGANIZE_IMPORTS_SILENTLY, fileURI); 48 | } else { 49 | commands.executeCommand(Commands.ORGANIZE_IMPORTS, { textDocument: { uri: fileURI } }); 50 | } 51 | } 52 | }); 53 | } 54 | 55 | let serverReady = false; 56 | 57 | export async function pasteFile(folder: fs.PathLike): Promise { 58 | const clipboardText: string = await env.clipboard.readText(); 59 | let filePath = folder.toString(); 60 | fs.stat(folder, async (err, stats) => { 61 | // If given path to selected folder is invalid (no folder is selected) 62 | if (filePath === clipboardText || stats.isFile() || (filePath === "." && workspace.workspaceFolders !== undefined)) { 63 | filePath = workspace.workspaceFolders[0].uri.fsPath; 64 | } 65 | if (!serverReady) { 66 | await apiManager.getApiInstance().serverReady().then( async () => { 67 | serverReady = true; 68 | }); 69 | } 70 | const fileString: string = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.RESOLVE_PASTED_TEXT, filePath, clipboardText); 71 | const fileUri = fileString !== null ? Uri.file(fileString) : null; 72 | if (fileUri !== null){ 73 | try { 74 | await workspace.fs.writeFile(fileUri, new TextEncoder().encode(clipboardText)); 75 | window.showTextDocument(fileUri, { preview: false }); 76 | } catch (error: unknown) { 77 | // Do nothing (file does not have write permissions) 78 | } 79 | } 80 | }); 81 | } -------------------------------------------------------------------------------- /src/pasteEventHandler.ts: -------------------------------------------------------------------------------- 1 | import { CancellationToken, commands, DataTransfer, DocumentPasteEdit as VDocumentPasteEdit, DocumentPasteEditProvider, DocumentPasteProviderMetadata, ExtensionContext, languages, Range, TextDocument, window, DocumentPasteEditContext, ProviderResult, DocumentPasteEdit, version } from "vscode"; 2 | import { FormattingOptions, Location, WorkspaceEdit as PWorkspaceEdit } from "vscode-languageclient"; 3 | import { LanguageClient } from "vscode-languageclient/node"; 4 | import { Commands } from "./commands"; 5 | import { JAVA_SELECTOR } from "./standardLanguageClient"; 6 | import * as semver from 'semver'; 7 | 8 | const TEXT_MIMETYPE: string = "text/plain"; 9 | const MIMETYPES: DocumentPasteProviderMetadata = { 10 | pasteMimeTypes: [TEXT_MIMETYPE], 11 | providedPasteEditKinds: [] 12 | }; 13 | 14 | /** 15 | * Parameters for `Commands.HANDLE_PASTE_EVENT` 16 | */ 17 | interface PasteEventParams { 18 | location: Location; 19 | text: string; 20 | formattingOptions: FormattingOptions; 21 | copiedDocumentUri?: string; 22 | } 23 | 24 | /** 25 | * Response from jdt.ls for `Commands.HANDLE_PASTE_EVENT` that's similar, but not identical, to VS Code's paste edit. 26 | * 27 | * @see VDocumentPasteEdit 28 | */ 29 | interface PDocumentPasteEdit { 30 | insertText: string; 31 | additionalEdit: PWorkspaceEdit; 32 | } 33 | 34 | /** 35 | * Registers the vscode-java DocumentPasteEditProviders and sets them up to be disposed. 36 | * 37 | * @param context the extension context 38 | */ 39 | export function registerPasteEventHandler(context: ExtensionContext, languageClient: LanguageClient) { 40 | if (languages["registerDocumentPasteEditProvider"]) { 41 | context.subscriptions.push(languages["registerDocumentPasteEditProvider"](JAVA_SELECTOR, new PasteEditProvider(languageClient), MIMETYPES)); 42 | } 43 | } 44 | 45 | /** 46 | * `DocumentPasteEditProvider` that delegates to jdt.ls to make any changes necessary to the pasted text and add any additional workspace edits. 47 | */ 48 | class PasteEditProvider implements DocumentPasteEditProvider { 49 | 50 | private languageClient: LanguageClient; 51 | private copiedContent: string | undefined; 52 | private copiedDocumentUri: string | undefined; 53 | 54 | constructor(languageClient: LanguageClient) { 55 | this.languageClient = languageClient; 56 | } 57 | 58 | async prepareDocumentPaste?(document: TextDocument, _ranges: readonly Range[], dataTransfer: DataTransfer, _token: CancellationToken): Promise { 59 | const copiedContent: string = await dataTransfer.get(TEXT_MIMETYPE).asString(); 60 | if (copiedContent) { 61 | this.copiedDocumentUri = document.uri.toString(); 62 | this.copiedContent = copiedContent; 63 | } 64 | } 65 | 66 | async provideDocumentPasteEdits?(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, context: DocumentPasteEditContext, token: CancellationToken): Promise { 67 | 68 | 69 | const insertText: string = await dataTransfer.get(TEXT_MIMETYPE).asString(); 70 | 71 | // don't try to provide for multi character inserts; the implementation will get messy and the feature won't be that helpful 72 | if (!insertText || (!!token && token.isCancellationRequested) || ranges.length !== 1) { 73 | return null; 74 | } 75 | 76 | const range = ranges[0]; 77 | 78 | const location: Location = { 79 | range: this.languageClient.code2ProtocolConverter.asRange(range), 80 | uri: document.uri.toString(), 81 | }; 82 | 83 | const activeTextEditor = window.activeTextEditor; 84 | 85 | const pasteEventParams: PasteEventParams = { 86 | location: location, 87 | text: insertText, 88 | copiedDocumentUri: this.copiedContent === insertText ? this.copiedDocumentUri : undefined, 89 | formattingOptions: { 90 | insertSpaces: activeTextEditor.options.insertSpaces, 91 | tabSize: activeTextEditor.options.tabSize 92 | } 93 | }; 94 | 95 | try { 96 | const pasteResponse: PDocumentPasteEdit = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.HANDLE_PASTE_EVENT, JSON.stringify(pasteEventParams)); 97 | if (pasteResponse) { 98 | const pasteEdit = { 99 | insertText: pasteResponse.insertText, 100 | additionalEdit: pasteResponse.additionalEdit ? await this.languageClient.protocol2CodeConverter.asWorkspaceEdit(pasteResponse.additionalEdit) : undefined 101 | }; 102 | if (semver.lt(version, '1.88.0')) { 103 | return pasteEdit as DocumentPasteEdit; 104 | } else { 105 | return [ pasteEdit ] as DocumentPasteEdit[]; 106 | } 107 | } 108 | } catch (e) { 109 | // Do nothing 110 | } 111 | // either the handler returns null or encounters problems, fall back to return undefined to let VS Code ignore this handler 112 | return undefined; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/pom/pomCodeActionProvider.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { CodeActionProvider, CodeAction, TextDocument, Range, Selection, CodeActionContext, CancellationToken, ProviderResult, Command, CodeActionKind, Diagnostic, WorkspaceEdit, EndOfLine, ExtensionContext, commands, CodeActionProviderMetadata, workspace, Uri, window, TextEditor } from "vscode"; 4 | import { Commands } from "../commands"; 5 | 6 | export class PomCodeActionProvider implements CodeActionProvider { 7 | constructor(context: ExtensionContext) { 8 | context.subscriptions.push(commands.registerCommand("_java.projectConfiguration.saveAndUpdate", async (uri: Uri) => { 9 | const document: TextDocument = await workspace.openTextDocument(uri); 10 | await document.save(); 11 | commands.executeCommand(Commands.CONFIGURATION_UPDATE, uri); 12 | })); 13 | } 14 | 15 | provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult<(Command | CodeAction)[]> { 16 | if (context?.diagnostics?.length && context.diagnostics[0].source === "Java") { 17 | return this.collectCodeActions(document, context.diagnostics); 18 | } 19 | 20 | return undefined; 21 | } 22 | 23 | collectCodeActions(document: TextDocument, diagnostics: readonly Diagnostic[]): CodeAction[] { 24 | const codeActions: CodeAction[] = []; 25 | for (const diagnostic of diagnostics) { 26 | if (diagnostic.message?.startsWith("Plugin execution not covered by lifecycle configuration")) { 27 | const indentation = this.getNewTextIndentation(document, diagnostic); 28 | const saveAndUpdateConfigCommand: Command = { 29 | title: "Save and reload project", 30 | command: "_java.projectConfiguration.saveAndUpdate", 31 | arguments: [document.uri], 32 | }; 33 | 34 | const action1 = new CodeAction("Enable this execution in project configuration phase", CodeActionKind.QuickFix.append("pom")); 35 | action1.edit = new WorkspaceEdit(); 36 | action1.edit.insert(document.uri, diagnostic.range.end, `${indentation}`); 37 | action1.command = saveAndUpdateConfigCommand; 38 | codeActions.push(action1); 39 | 40 | const action2 = new CodeAction("Enable this execution in project build phase", CodeActionKind.QuickFix.append("pom")); 41 | action2.edit = new WorkspaceEdit(); 42 | action2.edit.insert(document.uri, diagnostic.range.end, `${indentation}`); 43 | action2.command = saveAndUpdateConfigCommand; 44 | codeActions.push(action2); 45 | 46 | const action3 = new CodeAction("Mark this execution as ignored in pom.xml", CodeActionKind.QuickFix.append("pom")); 47 | action3.edit = new WorkspaceEdit(); 48 | action3.edit.insert(document.uri, diagnostic.range.end, `${indentation}`); 49 | action3.command = saveAndUpdateConfigCommand; 50 | codeActions.push(action3); 51 | } else if (diagnostic.message?.startsWith("The build file has been changed")) { 52 | const reloadProjectAction = new CodeAction("Reload project", CodeActionKind.QuickFix); 53 | reloadProjectAction.command = { 54 | title: "Reload Project", 55 | command: Commands.CONFIGURATION_UPDATE, 56 | arguments: [document.uri], 57 | }; 58 | codeActions.push(reloadProjectAction); 59 | } 60 | } 61 | 62 | return codeActions; 63 | } 64 | 65 | getNewTextIndentation(document: TextDocument, diagnostic: Diagnostic): string { 66 | const textline = document.lineAt(diagnostic.range.end.line); 67 | if (textline.text.lastIndexOf("") > diagnostic.range.end.character) { 68 | return ""; 69 | } 70 | 71 | let tabSize: number = 2; // default value 72 | let insertSpaces: boolean = true; // default value 73 | const activeEditor: TextEditor | undefined = window.activeTextEditor; 74 | if (activeEditor && activeEditor.document.uri.toString() === document.uri.toString()) { 75 | tabSize = Number(activeEditor.options.tabSize); 76 | insertSpaces = Boolean(activeEditor.options.insertSpaces); 77 | } 78 | 79 | const lineSeparator = document.eol === EndOfLine.LF ? "\r" : "\r\n"; 80 | let newIndentation = lineSeparator + textline.text.substring(0, textline.firstNonWhitespaceCharacterIndex); 81 | if (insertSpaces) { 82 | for (let i = 0; i < tabSize; i++) { 83 | newIndentation += ' '; // insert a space char. 84 | } 85 | } else { 86 | newIndentation += ' '; // insert a tab char. 87 | } 88 | 89 | return newIndentation; 90 | } 91 | } 92 | 93 | export const pomCodeActionMetadata: CodeActionProviderMetadata = { 94 | providedCodeActionKinds: [ 95 | CodeActionKind.QuickFix.append("pom") 96 | ], 97 | documentation: [ 98 | { 99 | kind: CodeActionKind.QuickFix.append("pom"), 100 | command: { 101 | title: "Learn more about not covered Maven execution", 102 | command: Commands.NOT_COVERED_EXECUTION 103 | } 104 | } 105 | ], 106 | }; 107 | -------------------------------------------------------------------------------- /src/serverStatus.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { EventEmitter } from "vscode"; 4 | import { serverTasks } from "./serverTasks"; 5 | import { serverStatusBarProvider } from "./serverStatusBarProvider"; 6 | 7 | export enum ServerStatusKind { 8 | ready = "Ready", 9 | warning = "Warning", 10 | error = "Error", 11 | busy = "Busy", 12 | } 13 | 14 | const emitter = new EventEmitter(); 15 | let status: ServerStatusKind = ServerStatusKind.ready; 16 | let isBusy: boolean = false; 17 | 18 | function fireEvent() { 19 | if (isBusy) { 20 | emitter.fire(ServerStatusKind.busy); 21 | return; 22 | } 23 | 24 | emitter.fire(status); 25 | } 26 | 27 | export namespace serverStatus { 28 | 29 | let hasError: boolean = false; 30 | 31 | export const onServerStatusChanged = emitter.event; 32 | 33 | export function initialize() { 34 | serverTasks.onDidUpdateServerTask(tasks => { 35 | const busyTask = tasks.find(task => !task.complete); 36 | isBusy = !!busyTask; 37 | if (isBusy) { 38 | serverStatusBarProvider.setBusy(busyTask.value.message); 39 | } 40 | fireEvent(); 41 | }); 42 | } 43 | 44 | export function updateServerStatus(newStatus: ServerStatusKind) { 45 | if (newStatus === ServerStatusKind.busy) { 46 | throw new Error("Busy status cannot be set directly."); 47 | } 48 | 49 | if (newStatus === ServerStatusKind.error || newStatus === ServerStatusKind.warning) { 50 | hasError = true; 51 | } else if (hasError) { 52 | return; 53 | } 54 | 55 | status = newStatus; 56 | fireEvent(); 57 | } 58 | 59 | export function hasErrors() { 60 | return hasError; 61 | } 62 | 63 | export function errorResolved() { 64 | hasError = false; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/serverStatusBarProvider.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { StatusBarItem, window, StatusBarAlignment, ThemeColor, commands, QuickPickItem, QuickPickItemKind } from "vscode"; 4 | import { Disposable } from "vscode-languageclient"; 5 | import { StatusCommands } from "./languageStatusItemFactory"; 6 | import { Commands } from "./commands"; 7 | import { ServerStatusKind } from "./serverStatus"; 8 | 9 | class ServerStatusBarProvider implements Disposable { 10 | private statusBarItem: StatusBarItem; 11 | 12 | constructor() { 13 | this.statusBarItem = window.createStatusBarItem("java.serverStatus", StatusBarAlignment.Left); 14 | this.statusBarItem.show(); 15 | } 16 | 17 | public showLightWeightStatus(): void { 18 | this.statusBarItem.name = "Java Server Mode"; 19 | this.statusBarItem.text = `${StatusIcon.lightWeight} Java: Lightweight Mode`; 20 | this.statusBarItem.command = StatusCommands.switchToStandardCommand; 21 | this.statusBarItem.tooltip = "Java language server is running in LightWeight mode, click to switch to Standard mode"; 22 | } 23 | 24 | public showNotImportedStatus(): void { 25 | this.statusBarItem.name = "No projects are imported"; 26 | this.statusBarItem.text = `${StatusIcon.notImported} Java: No Projects Imported`; 27 | this.statusBarItem.command = StatusCommands.startStandardServerCommand; 28 | this.statusBarItem.tooltip = "No projects are imported, click to load projects"; 29 | } 30 | 31 | public setBusy(process: string): void { 32 | this.statusBarItem.text = `${StatusIcon.busy} Java: ${process}`; 33 | this.statusBarItem.tooltip = process; 34 | this.statusBarItem.command = { 35 | title: "Show Java status menu", 36 | command: Commands.OPEN_STATUS_SHORTCUT, 37 | tooltip: "Show Java status menu", 38 | arguments: [ServerStatusKind.busy], 39 | }; 40 | } 41 | 42 | public setError(): void { 43 | this.statusBarItem.text = `${StatusIcon.java} Java: Error`; 44 | this.statusBarItem.tooltip = "Show Java status menu"; 45 | this.statusBarItem.command = { 46 | title: "Show Java status menu", 47 | command: Commands.OPEN_STATUS_SHORTCUT, 48 | tooltip: "Show Java status menu", 49 | arguments: [ServerStatusKind.error], 50 | }; 51 | } 52 | 53 | public setWarning(): void { 54 | this.statusBarItem.text = `${StatusIcon.java} Java: Warning`; 55 | this.statusBarItem.tooltip = "Show Java status menu"; 56 | this.statusBarItem.command = { 57 | title: "Show Java status menu", 58 | command: Commands.OPEN_STATUS_SHORTCUT, 59 | tooltip: "Show Java status menu", 60 | arguments: [ServerStatusKind.warning], 61 | }; 62 | } 63 | 64 | public setReady(): void { 65 | this.statusBarItem.text = `${StatusIcon.java} Java: Ready`; 66 | this.statusBarItem.tooltip = "Show Java status menu"; 67 | this.statusBarItem.command = { 68 | title: "Show Java status menu", 69 | command: Commands.OPEN_STATUS_SHORTCUT, 70 | tooltip: "Show Java status menu", 71 | arguments: ["Ready"], 72 | }; 73 | } 74 | 75 | public dispose(): void { 76 | this.statusBarItem?.dispose(); 77 | } 78 | } 79 | 80 | export enum StatusIcon { 81 | lightWeight = "$(rocket)", 82 | notImported = "$(info)", 83 | busy = "$(sync~spin)", 84 | java = "$(coffee)", 85 | } 86 | 87 | export interface ShortcutQuickPickItem extends QuickPickItem { 88 | command: string; 89 | args?: any[]; 90 | } 91 | 92 | export const serverStatusBarProvider: ServerStatusBarProvider = new ServerStatusBarProvider(); 93 | -------------------------------------------------------------------------------- /src/serverTasks.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "vscode"; 2 | import { ProgressReport, ProgressKind } from "./protocol"; 3 | 4 | const findIndex = require("lodash.findindex"); 5 | 6 | let tasks: ProgressReport[] = []; 7 | 8 | const emitter = new EventEmitter(); 9 | let suggestedTaskEntrySize = 10; 10 | 11 | export namespace serverTasks { 12 | export const onDidUpdateServerTask = emitter.event; 13 | 14 | export function updateServerTask(report: ProgressReport) { 15 | applyReport(report); 16 | emitter.fire(tasks); 17 | } 18 | 19 | export function suggestTaskEntrySize(size: number) { 20 | if (size > suggestedTaskEntrySize) { 21 | suggestedTaskEntrySize = size; 22 | } 23 | } 24 | 25 | export function getHistory(): ProgressReport[] { 26 | return tasks; 27 | } 28 | } 29 | 30 | function organizeTasks() { 31 | let newArray = tasks; 32 | if (tasks.length > suggestedTaskEntrySize) { 33 | newArray = recycleTasks(tasks, suggestedTaskEntrySize); 34 | } 35 | 36 | // make sure in-progress items are always at the end 37 | newArray.sort((a, b) => Number(b.complete) - Number(a.complete)); 38 | 39 | tasks = newArray; 40 | } 41 | 42 | function recycleTasks(tasks: ProgressReport[], length: number) { 43 | const newArray = [], delta = tasks.length - length; 44 | let skipped = 0; 45 | 46 | tasks.forEach(task => { 47 | if (skipped < delta && task.complete) { 48 | skipped++; 49 | return; 50 | } 51 | 52 | newArray.push(task); 53 | }); 54 | 55 | return newArray; 56 | } 57 | 58 | function applyReport(report: ProgressReport) { 59 | const index = findIndex(tasks, task => task.token === report.token); 60 | if (index === -1) { 61 | tasks.push(report); 62 | } else { 63 | tasks[index] = report; 64 | } 65 | 66 | organizeTasks(); 67 | } 68 | -------------------------------------------------------------------------------- /src/smartSemicolonDetection.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { commands, Position, Range, Selection, window } from 'vscode'; 4 | import { Commands } from './commands'; 5 | import { getJavaConfiguration } from './utils'; 6 | let active = false; 7 | 8 | export async function smartSemicolonDetection() { 9 | if (enabled()) { 10 | const params: SmartDetectionParams = { 11 | uri: window.activeTextEditor.document.uri.toString(), 12 | position: window.activeTextEditor!.selection.active, 13 | }; 14 | setActive(true); 15 | const response: SmartDetectionParams = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.SMARTSEMICOLON_DETECTION, JSON.stringify(params)); 16 | if (response !== null) { 17 | window.activeTextEditor!.edit(editBuilder => { 18 | const oldPosition = window.activeTextEditor!.selection.active; 19 | editBuilder.delete(new Range(new Position(oldPosition.line, oldPosition.character - 1), oldPosition)); 20 | const newPos = new Position(response.position.line, response.position.character + 1); 21 | editBuilder.insert(newPos, ";"); 22 | window.activeTextEditor.selections = [new Selection(newPos, newPos)]; 23 | // newPosition = window.activeTextEditor!.selection.active; 24 | }); 25 | return; 26 | } 27 | } 28 | } 29 | 30 | interface SmartDetectionParams { 31 | uri: String; 32 | position: Position; 33 | } 34 | 35 | function enabled() { 36 | return getJavaConfiguration().get("edit.smartSemicolonDetection.enabled"); 37 | } 38 | 39 | export function isActive() { 40 | return active; 41 | } 42 | 43 | export function setActive(pActive: boolean) { 44 | active = pActive; 45 | } -------------------------------------------------------------------------------- /src/snippetCompletionProvider.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { CompletionItemProvider, TextDocument, Position, CancellationToken, CompletionContext, CompletionItem, CompletionItemKind, SnippetString, MarkdownString, languages, Disposable } from "vscode"; 4 | import * as fse from 'fs-extra'; 5 | import * as path from 'path'; 6 | 7 | class SnippetCompletionProvider implements Disposable { 8 | 9 | private providerImpl: Disposable; 10 | 11 | public initialize(): SnippetCompletionProvider { 12 | this.providerImpl = languages.registerCompletionItemProvider({ scheme: 'file', language: 'java' }, new SnippetCompletionProviderImpl()); 13 | return this; 14 | } 15 | 16 | public dispose(): void { 17 | if (this.providerImpl) { 18 | this.providerImpl.dispose(); 19 | } 20 | } 21 | } 22 | 23 | class SnippetCompletionProviderImpl implements CompletionItemProvider { 24 | 25 | private snippets: {}; 26 | 27 | public constructor() { 28 | this.snippets = fse.readJSONSync(path.join(__dirname, '..', 'snippets', 'server.json')); 29 | } 30 | 31 | public async provideCompletionItems(_document: TextDocument, _position: Position, _token: CancellationToken, _context: CompletionContext): Promise { 32 | 33 | const snippetItems: CompletionItem[] = []; 34 | for (const label of Object.keys(this.snippets)) { 35 | const snippetContent = this.snippets[label]; 36 | if (Array.isArray(snippetContent.prefix)) { 37 | for (const prefix of snippetContent.prefix) { 38 | snippetItems.push(this.getCompletionItem(prefix, snippetContent)); 39 | } 40 | } else { 41 | snippetItems.push(this.getCompletionItem(snippetContent.prefix, snippetContent)); 42 | } 43 | } 44 | return snippetItems; 45 | } 46 | 47 | private getCompletionItem(prefix: string, snippetContent: any) { 48 | const snippetItem: CompletionItem = new CompletionItem(prefix); 49 | snippetItem.kind = CompletionItemKind.Snippet; 50 | snippetItem.detail = snippetContent.description; 51 | const insertText: string = (snippetContent.body as String[]).join('\n'); 52 | snippetItem.insertText = new SnippetString(insertText); 53 | snippetItem.documentation = beautifyDocument(insertText); 54 | return snippetItem; 55 | } 56 | } 57 | 58 | export function beautifyDocument(raw: string): MarkdownString { 59 | const escapedString = raw.replace(/\$\{\d\|(.*?),.*?\}/gm, '$1') 60 | .replace(/\$\{\d:?(.*?)\}/gm, '$1') 61 | .replace(/\$\d/gm, '') 62 | .replace(/\${TM_SELECTED_TEXT:}/gm, '') 63 | .replace(/\${TM_FILENAME_BASE}/gm, ''); 64 | return new MarkdownString().appendCodeblock(escapedString, "java"); 65 | } 66 | 67 | export const snippetCompletionProvider: SnippetCompletionProvider = new SnippetCompletionProvider(); 68 | -------------------------------------------------------------------------------- /src/syntaxLanguageClient.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as net from "net"; 4 | import { DidChangeConfigurationNotification, LanguageClientOptions } from "vscode-languageclient"; 5 | import { LanguageClient, ServerOptions, StreamInfo } from "vscode-languageclient/node"; 6 | import { apiManager } from "./apiManager"; 7 | import { ClientErrorHandler } from "./clientErrorHandler"; 8 | import { ClientStatus } from "./extension.api"; 9 | import { logger } from "./log"; 10 | import { OutputInfoCollector } from "./outputInfoCollector"; 11 | import { StatusNotification } from "./protocol"; 12 | import { RequirementsData } from "./requirements"; 13 | import { ServerMode } from "./settings"; 14 | import { snippetCompletionProvider } from "./snippetCompletionProvider"; 15 | import { getJavaConfig } from "./utils"; 16 | import { DEBUG } from "./javaServerStarter"; 17 | import { TracingLanguageClient } from "./TracingLanguageClient"; 18 | 19 | const extensionName = "Language Support for Java (Syntax Server)"; 20 | 21 | export class SyntaxLanguageClient { 22 | private languageClient: LanguageClient; 23 | private status: ClientStatus = ClientStatus.uninitialized; 24 | 25 | public initialize(requirements: RequirementsData, clientOptions: LanguageClientOptions, serverOptions?: ServerOptions) { 26 | const newClientOptions: LanguageClientOptions = Object.assign({}, clientOptions, { 27 | middleware: { 28 | workspace: { 29 | didChangeConfiguration: async () => { 30 | await this.languageClient.sendNotification(DidChangeConfigurationNotification.type, { 31 | settings: { 32 | java: await getJavaConfig(requirements.java_home), 33 | } 34 | }); 35 | } 36 | } 37 | }, 38 | errorHandler: new ClientErrorHandler(extensionName), 39 | initializationFailedHandler: error => { 40 | logger.error(`Failed to initialize ${extensionName} due to ${error && error.toString()}`); 41 | if (error.toString().includes('Connection') && error.toString().includes('disposed')) { 42 | return false; 43 | } else { 44 | return true; 45 | } 46 | }, 47 | outputChannel: new OutputInfoCollector(extensionName), 48 | outputChannelName: extensionName 49 | }); 50 | 51 | const lsPort = process.env['SYNTAXLS_CLIENT_PORT']; 52 | if (!serverOptions && lsPort) { 53 | serverOptions = () => { 54 | const socket = net.connect(lsPort); 55 | const result: StreamInfo = { 56 | writer: socket, 57 | reader: socket 58 | }; 59 | return Promise.resolve(result); 60 | }; 61 | } 62 | 63 | if (serverOptions) { 64 | this.languageClient = new TracingLanguageClient('java', extensionName, serverOptions, newClientOptions, DEBUG); 65 | } 66 | 67 | this.status = ClientStatus.initialized; 68 | } 69 | 70 | public registerSyntaxClientActions(serverOptions?: ServerOptions): void { 71 | // TODO: Currently only resolve the promise when the server mode is explicitly set to lightweight. 72 | // This is to avoid breakings 73 | if (serverOptions) { 74 | this.languageClient.onNotification(StatusNotification.type, (report) => { 75 | switch (report.type) { 76 | case 'Started': 77 | this.status = ClientStatus.started; 78 | apiManager.updateStatus(ClientStatus.started); 79 | // Disable the client-side snippet provider since LS is ready. 80 | snippetCompletionProvider.dispose(); 81 | break; 82 | case 'Error': 83 | this.status = ClientStatus.error; 84 | apiManager.updateStatus(ClientStatus.error); 85 | break; 86 | default: 87 | break; 88 | } 89 | if (apiManager.getApiInstance().serverMode === ServerMode.lightWeight) { 90 | apiManager.fireDidServerModeChange(ServerMode.lightWeight); 91 | } 92 | }); 93 | } 94 | } 95 | 96 | public start(): Promise { 97 | if (this.languageClient) { 98 | this.status = ClientStatus.starting; 99 | return this.languageClient.start(); 100 | } 101 | } 102 | 103 | public stop(): Promise { 104 | this.status = ClientStatus.stopping; 105 | if (this.languageClient) { 106 | try { 107 | return this.languageClient.stop(); 108 | } finally { 109 | this.languageClient = null; 110 | } 111 | } 112 | return Promise.resolve(); 113 | } 114 | 115 | public isAlive(): boolean { 116 | return !!this.languageClient && this.status !== ClientStatus.stopping; 117 | } 118 | 119 | public getClient(): LanguageClient { 120 | return this.languageClient; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/telemetry.ts: -------------------------------------------------------------------------------- 1 | import { TelemetryService, getRedHatService } from "@redhat-developer/vscode-redhat-telemetry"; 2 | import { ExtensionContext, workspace, WorkspaceConfiguration } from "vscode"; 3 | 4 | /** 5 | * Wrap vscode-redhat-telemetry to suit vscode-java 6 | */ 7 | export namespace Telemetry { 8 | 9 | export const STARTUP_EVT = "startup"; 10 | export const COMPLETION_EVENT = "textCompletion"; 11 | export const SERVER_INITIALIZED_EVT = "java.workspace.initialized"; 12 | export const LS_ERROR = "java.ls.error"; 13 | let telemetryManager: TelemetryService = null; 14 | 15 | /** 16 | * Starts the telemetry service 17 | * 18 | * @returns when the telemetry service has been started 19 | * @throws Error if the telemetry service has already been started 20 | */ 21 | export async function startTelemetry(context: ExtensionContext): Promise { 22 | if (!!telemetryManager) { 23 | throw new Error("The telemetry service for vscode-java has already been started"); 24 | } 25 | const redhatService = await getRedHatService(context); 26 | const telemService = await redhatService.getTelemetryService(); 27 | telemetryManager = telemService; 28 | return telemService; 29 | } 30 | 31 | /** 32 | * Send a telemetry event with the given name and data 33 | * 34 | * @param eventName the name of the telemetry event 35 | * @param data the telemetry data 36 | * @throws Error if the telemetry service has not been started yet 37 | */ 38 | export async function sendTelemetry(eventName: string, data?: any): Promise { 39 | if (!telemetryManager) { 40 | throw new Error("The telemetry service for vscode-java has not been started yet"); 41 | } 42 | const javaSettings = getJavaSettingsForTelemetry(workspace.getConfiguration()); 43 | 44 | let properties: any; 45 | if (eventName === STARTUP_EVT) { 46 | properties= { ...data, ...javaSettings }; 47 | } else { 48 | properties= { ...data}; 49 | } 50 | 51 | return telemetryManager.send({ 52 | name: eventName, 53 | properties 54 | }); 55 | } 56 | 57 | function getJavaSettingsForTelemetry(config: WorkspaceConfiguration) { 58 | // settings whose values we can record 59 | const SETTINGS_BASIC = [ 60 | "java.quickfix.showAt", "java.symbols.includeSourceMethodDeclarations", 61 | "java.completion.collapseCompletionItems", "java.completion.guessMethodArguments", 62 | "java.cleanup.actionsOnSave", "java.completion.postfix.enabled", 63 | "java.sharedIndexes.enabled", "java.inlayHints.parameterNames.enabled", 64 | "java.server.launchMode", "java.autobuild.enabled" 65 | ]; 66 | // settings where we only record their existence 67 | const SETTINGS_CUSTOM = [ 68 | "java.settings.url", "java.format.settings.url" 69 | ]; 70 | 71 | let value: any; 72 | const properties = {}; 73 | 74 | for (const key of SETTINGS_CUSTOM) { 75 | if (config.get(key)) { 76 | properties[key] = true; 77 | } 78 | } 79 | for (const key of SETTINGS_BASIC) { 80 | value = config.get(key); 81 | if (value !== undefined) { 82 | properties[key] = value; 83 | } 84 | } 85 | 86 | return properties; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/themeUtils.ts: -------------------------------------------------------------------------------- 1 | import { SymbolKind as VSymbolKind, ThemeIcon } from "vscode"; 2 | import { SymbolKind as LSymbolKind} from "vscode-languageclient"; 3 | 4 | const themeIconIds = [ 5 | 'symbol-file', 'symbol-module', 'symbol-namespace', 'symbol-package', 'symbol-class', 'symbol-method', 6 | 'symbol-property', 'symbol-field', 'symbol-constructor', 'symbol-enum', 'symbol-interface', 7 | 'symbol-function', 'symbol-variable', 'symbol-constant', 'symbol-string', 'symbol-number', 'symbol-boolean', 8 | 'symbol-array', 'symbol-object', 'symbol-key', 'symbol-null', 'symbol-enum-member', 'symbol-struct', 9 | 'symbol-event', 'symbol-operator', 'symbol-type-parameter' 10 | ]; 11 | 12 | export function getLThemeIcon(kind: LSymbolKind): ThemeIcon | undefined { 13 | const id = themeIconIds[kind - 1]; 14 | return id ? new ThemeIcon(id) : undefined; 15 | } 16 | 17 | export function getThemeIcon(kind: VSymbolKind): ThemeIcon | undefined { 18 | const id = themeIconIds[kind]; 19 | return id ? new ThemeIcon(id) : undefined; 20 | } -------------------------------------------------------------------------------- /src/typeHierarchy/protocol.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { Range, SymbolKind } from "vscode-languageclient"; 3 | 4 | export enum TypeHierarchyDirection { 5 | children, 6 | parents, 7 | both 8 | } 9 | 10 | export class LSPTypeHierarchyItem { 11 | name: string; 12 | detail: string; 13 | kind: SymbolKind; 14 | deprecated: boolean; 15 | uri: string; 16 | range: Range; 17 | selectionRange: Range; 18 | parents: LSPTypeHierarchyItem[]; 19 | children: LSPTypeHierarchyItem[]; 20 | data: any; 21 | } 22 | 23 | export class TypeHierarchyItem { 24 | name: string; 25 | detail: string; 26 | kind: vscode.SymbolKind; 27 | deprecated: boolean; 28 | uri: string; 29 | range: vscode.Range; 30 | selectionRange: vscode.Range; 31 | parents: TypeHierarchyItem[]; 32 | children: TypeHierarchyItem[]; 33 | data: any; 34 | expand: boolean; 35 | } 36 | -------------------------------------------------------------------------------- /src/typeHierarchy/references-view.d.ts: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | 8 | /** 9 | * This interface describes the shape for the references viewlet API. It consists 10 | * of a single `setInput` function which must be called with a full implementation 11 | * of the `SymbolTreeInput`-interface. To acquire this API use the default mechanics, e.g: 12 | * 13 | * ```ts 14 | * // get references viewlet API 15 | * const api = await vscode.extensions.getExtension('ms-vscode.references-view').activate(); 16 | * 17 | * // instantiate and set input which updates the view 18 | * const myInput: SymbolTreeInput = ... 19 | * api.setInput(myInput) 20 | * ``` 21 | */ 22 | export interface SymbolTree { 23 | 24 | /** 25 | * Set the contents of the references viewlet. 26 | * 27 | * @param input A symbol tree input object 28 | */ 29 | setInput(input: SymbolTreeInput): void; 30 | } 31 | 32 | /** 33 | * A symbol tree input is the entry point for populating the references viewlet. 34 | * Inputs must be anchored at a code location, they must have a title, and they 35 | * must resolve to a model. 36 | */ 37 | export interface SymbolTreeInput { 38 | 39 | /** 40 | * The value of the `reference-list.source` context key. Use this to control 41 | * input dependent commands. 42 | */ 43 | readonly contextValue: string; 44 | 45 | /** 46 | * The (short) title of this input, like "Implementations" or "Callers Of" 47 | */ 48 | readonly title: string; 49 | 50 | /** 51 | * The location at which this position is anchored. Locations are validated and inputs 52 | * with "funny" locations might be ignored 53 | */ 54 | readonly location: vscode.Location; 55 | 56 | /** 57 | * Resolve this input to a model that contains the actual data. When there are no result 58 | * than `undefined` or `null` should be returned. 59 | */ 60 | resolve(): vscode.ProviderResult>; 61 | 62 | /** 63 | * This function is called when re-running from history. The symbols tree has tracked 64 | * the original location of this input and that is now passed to this input. The 65 | * implementation of this function should return a clone where the `location`-property 66 | * uses the provided `location` 67 | * 68 | * @param location The location at which the new input should be anchored. 69 | * @returns A new input which location is anchored at the position. 70 | */ 71 | with(location: vscode.Location): SymbolTreeInput; 72 | } 73 | 74 | /** 75 | * A symbol tree model which is used to populate the symbols tree. 76 | */ 77 | export interface SymbolTreeModel { 78 | 79 | /** 80 | * A tree data provider which is used to populate the symbols tree. 81 | */ 82 | provider: vscode.TreeDataProvider; 83 | 84 | /** 85 | * An optional message that is displayed above the tree. Whenever the provider 86 | * fires a change event this message is read again. 87 | */ 88 | message: string | undefined; 89 | 90 | /** 91 | * Optional support for symbol navigation. When implemented, navigation commands like 92 | * "Go to Next" and "Go to Previous" will be working with this model. 93 | */ 94 | navigation?: SymbolItemNavigation; 95 | 96 | /** 97 | * Optional support for editor highlights. WHen implemented, the editor will highlight 98 | * symbol ranges in the source code. 99 | */ 100 | highlights?: SymbolItemEditorHighlights; 101 | 102 | /** 103 | * Optional dispose function which is invoked when this model is 104 | * needed anymore 105 | */ 106 | dispose?(): void; 107 | } 108 | 109 | /** 110 | * Interface to support the built-in symbol navigation. 111 | */ 112 | export interface SymbolItemNavigation { 113 | /** 114 | * Return the item that is the nearest to the given location or `undefined` 115 | */ 116 | nearest(uri: vscode.Uri, position: vscode.Position): T | undefined; 117 | /** 118 | * Return the next item from the given item or the item itself. 119 | */ 120 | next(from: T): T; 121 | /** 122 | * Return the previous item from the given item or the item itself. 123 | */ 124 | previous(from: T): T; 125 | /** 126 | * Return the location of the given item. 127 | */ 128 | location(item: T): vscode.Location | undefined; 129 | } 130 | 131 | /** 132 | * Interface to support the built-in editor highlights. 133 | */ 134 | export interface SymbolItemEditorHighlights { 135 | /** 136 | * Given an item and an uri return an array of ranges to highlight. 137 | */ 138 | getEditorHighlights(item: T, uri: vscode.Uri): vscode.Range[] | undefined; 139 | } 140 | -------------------------------------------------------------------------------- /src/typeHierarchy/typeHierarchyTree.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { Position, TextDocumentIdentifier, TextDocumentPositionParams } from "vscode-languageclient"; 3 | import { LanguageClient } from "vscode-languageclient/node"; 4 | import { Commands } from "../commands"; 5 | import { getActiveLanguageClient } from "../extension"; 6 | import { showNoLocationFound } from "../standardLanguageClient"; 7 | import { TypeHierarchyTreeInput } from "./model"; 8 | import { LSPTypeHierarchyItem, TypeHierarchyDirection, TypeHierarchyItem } from "./protocol"; 9 | import { SymbolTree } from "./references-view"; 10 | import { toTypeHierarchyItem } from "./util"; 11 | 12 | export class TypeHierarchyTree { 13 | private api: SymbolTree; 14 | private direction: TypeHierarchyDirection; 15 | private client: LanguageClient; 16 | private cancelTokenSource: vscode.CancellationTokenSource; 17 | private location: vscode.Location; 18 | private baseItem: TypeHierarchyItem; 19 | public initialized: boolean; 20 | 21 | constructor() { 22 | this.initialized = false; 23 | } 24 | 25 | public async initialize() { 26 | // It uses a new publisher id in June 2022 Update, check both old/new id for compatibility 27 | // See https://github.com/microsoft/vscode/pull/152213 28 | const referencesViewExt = vscode.extensions.getExtension('vscode.references-view') 29 | ?? vscode.extensions.getExtension('ms-vscode.references-view'); 30 | this.api = await referencesViewExt?.activate(); 31 | this.client = await getActiveLanguageClient(); 32 | this.initialized = true; 33 | } 34 | 35 | public async setTypeHierarchy(location: vscode.Location, direction: TypeHierarchyDirection): Promise { 36 | if (!this.initialized) { 37 | await this.initialize(); 38 | } 39 | if (!this.api) { 40 | return; 41 | } 42 | if (this.cancelTokenSource) { 43 | this.cancelTokenSource.cancel(); 44 | } 45 | this.cancelTokenSource = new vscode.CancellationTokenSource(); 46 | const textDocument: TextDocumentIdentifier = TextDocumentIdentifier.create(location.uri.toString()); 47 | const position: Position = Position.create(location.range.start.line, location.range.start.character); 48 | const params: TextDocumentPositionParams = { 49 | textDocument: textDocument, 50 | position: position, 51 | }; 52 | let lspItem: LSPTypeHierarchyItem; 53 | try { 54 | lspItem = await vscode.commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.OPEN_TYPE_HIERARCHY, JSON.stringify(params), JSON.stringify(direction), JSON.stringify(0), this.cancelTokenSource.token); 55 | } catch (e) { 56 | // operation cancelled 57 | return; 58 | } 59 | if (!lspItem) { 60 | showNoLocationFound('No Type Hierarchy found'); 61 | return; 62 | } 63 | const symbolKind = this.client.protocol2CodeConverter.asSymbolKind(lspItem.kind); 64 | if (direction === TypeHierarchyDirection.both && symbolKind === vscode.SymbolKind.Interface) { 65 | direction = TypeHierarchyDirection.children; 66 | } 67 | const item: TypeHierarchyItem = toTypeHierarchyItem(this.client, lspItem, direction); 68 | const input: TypeHierarchyTreeInput = new TypeHierarchyTreeInput(location, direction, this.cancelTokenSource.token, item); 69 | this.location = location; 70 | this.direction = direction; 71 | this.baseItem = item; 72 | this.api.setInput(input); 73 | } 74 | 75 | public changeDirection(direction: TypeHierarchyDirection): void { 76 | if (!this.api) { 77 | return; 78 | } 79 | if (this.cancelTokenSource) { 80 | this.cancelTokenSource.cancel(); 81 | } 82 | this.cancelTokenSource = new vscode.CancellationTokenSource(); 83 | this.baseItem.children = undefined; 84 | this.baseItem.parents = undefined; 85 | const input: TypeHierarchyTreeInput = new TypeHierarchyTreeInput(this.location, direction, this.cancelTokenSource.token, this.baseItem); 86 | this.direction = direction; 87 | this.api.setInput(input); 88 | } 89 | 90 | public async changeBaseItem(item: TypeHierarchyItem): Promise { 91 | if (!this.api) { 92 | return; 93 | } 94 | if (this.cancelTokenSource) { 95 | this.cancelTokenSource.cancel(); 96 | } 97 | this.cancelTokenSource = new vscode.CancellationTokenSource(); 98 | item.parents = undefined; 99 | item.children = undefined; 100 | const location: vscode.Location = new vscode.Location(vscode.Uri.parse(item.uri), item.selectionRange); 101 | const newLocation: vscode.Location = (await this.isValidRequestPosition(location.uri, location.range.start)) ? location : this.location; 102 | const input: TypeHierarchyTreeInput = new TypeHierarchyTreeInput(newLocation, this.direction, this.cancelTokenSource.token, item); 103 | this.location = newLocation; 104 | this.baseItem = item; 105 | this.api.setInput(input); 106 | } 107 | 108 | private async isValidRequestPosition(uri: vscode.Uri, position: vscode.Position) { 109 | const doc = await vscode.workspace.openTextDocument(uri); 110 | let range = doc.getWordRangeAtPosition(position); 111 | if (!range) { 112 | range = doc.getWordRangeAtPosition(position, /[^\s]+/); 113 | } 114 | return Boolean(range); 115 | } 116 | } 117 | 118 | export const typeHierarchyTree: TypeHierarchyTree = new TypeHierarchyTree(); 119 | -------------------------------------------------------------------------------- /src/webview/changeSignature/App.css: -------------------------------------------------------------------------------- 1 | @import "../../../node_modules/@vscode/codicons/dist/codicon.css"; 2 | 3 | main { 4 | max-width: 600px; 5 | margin-left: auto; 6 | margin-right: auto; 7 | } 8 | 9 | .section { 10 | margin-bottom: 0; 11 | } 12 | 13 | .section-columns { 14 | display: flex; 15 | width: 100%; 16 | padding-left: calc(var(--design-unit) * 1px); 17 | } 18 | 19 | .text-title { 20 | margin: 0; 21 | height: calc(var(--input-height) * 0.8px); 22 | } 23 | 24 | .text-title-content { 25 | padding: 0 0 0 calc(var(--design-unit) * 1px); 26 | margin: 0.5rem 0 0 0; 27 | height: calc(var(--input-height) * 0.8px); 28 | } 29 | 30 | .header-left { 31 | flex-direction: column; 32 | display: flex; 33 | padding: 0.5rem 0.5rem 0.5rem 0; 34 | flex: 0 0 33%; 35 | box-sizing: border-box; 36 | } 37 | 38 | .header { 39 | flex-direction: column; 40 | display: flex; 41 | padding: 0.5rem; 42 | flex: 0 0 33%; 43 | box-sizing: border-box; 44 | } 45 | 46 | .header-right { 47 | flex-direction: column; 48 | display: flex; 49 | padding: 0.5rem 0 0.5rem 0.5rem; 50 | flex: 0 0 33%; 51 | box-sizing: border-box; 52 | } 53 | 54 | .vsc-button-left { 55 | float: left; 56 | margin: 0 0.5rem 0.5rem 0; 57 | } 58 | 59 | .vsc-button { 60 | float: left; 61 | margin: 0 0.5rem 0.5rem 0.5rem; 62 | } 63 | 64 | .add-button { 65 | margin: 0.5rem 0 0 0; 66 | } 67 | 68 | .bottom-buttons { 69 | padding: 0 0 0 calc(var(--design-unit) * 1px); 70 | margin: 0.5rem 0 0 0; 71 | } 72 | 73 | .preview { 74 | padding: 0 0 0 calc(var(--design-unit) * 1px); 75 | margin: 0 0 0.5rem 0; 76 | width: 99%; 77 | } 78 | 79 | .parameters-panel { 80 | margin: 0; 81 | width: calc(99% + 4px); 82 | } 83 | 84 | .parameters-view { 85 | padding: 0 0 0 calc(var(--design-unit) * 1px); 86 | flex-direction: column; 87 | } 88 | 89 | .parameter-cell { 90 | padding-left: 0; 91 | pointer-events: none; 92 | } 93 | 94 | .parameter-cell-title { 95 | padding-left: 0; 96 | } 97 | 98 | .parameter-cell-title:focus { 99 | border-color: var(--vscode-keybindingTable-headerBackground); 100 | background-color: inherit; 101 | color: inherit; 102 | } 103 | 104 | .parameter-cell-header { 105 | background-color: var(--vscode-keybindingTable-headerBackground); 106 | } 107 | 108 | .parameter-cell-edit { 109 | padding-left: 0; 110 | background-color: var(--vscode-input-background); 111 | } 112 | 113 | .parameter-cell-edit-button { 114 | padding: 0; 115 | border: 0; 116 | display: flex; 117 | justify-content: right; 118 | background-color: var(--vscode-input-background); 119 | } 120 | 121 | .parameter-cell-button { 122 | padding: 0; 123 | border: 0; 124 | display: flex; 125 | justify-content: right; 126 | } 127 | 128 | .parameter-cell-button:focus { 129 | background-color: inherit; 130 | } 131 | 132 | .parameter-cell-button:active { 133 | background-color: inherit; 134 | } 135 | 136 | .table-buttons { 137 | display: inline-flex; 138 | } 139 | 140 | .table-buttons-edit { 141 | display: inline-flex; 142 | background-color: var(--vscode-editor-background); 143 | } 144 | 145 | .table-buttons-edit-ok { 146 | margin-left: 0.2rem; 147 | margin-right: 0.2rem; 148 | } 149 | 150 | .table-buttons-edit-cancel { 151 | margin: 0; 152 | } 153 | 154 | .delegate { 155 | padding: 0 0 0 calc(var(--design-unit) * 1px); 156 | margin: 0.5rem 0 0.5rem 0; 157 | } 158 | -------------------------------------------------------------------------------- /src/webview/changeSignature/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { App } from "./App"; 4 | 5 | ReactDOM.render( 6 | 7 | 8 | , 9 | document.getElementById("root") 10 | ); 11 | -------------------------------------------------------------------------------- /src/webview/vscodeApiWrapper.ts: -------------------------------------------------------------------------------- 1 | import type { WebviewApi } from "vscode-webview"; 2 | 3 | /** 4 | * A utility wrapper around the acquireVsCodeApi() function, which enables 5 | * message passing and state management between the webview and extension 6 | * contexts. 7 | * 8 | * This utility also enables webview code to be run in a web browser-based 9 | * dev server by using native web browser features that mock the functionality 10 | * enabled by acquireVsCodeApi. 11 | */ 12 | class VSCodeAPIWrapper { 13 | private readonly vsCodeApi: WebviewApi | undefined; 14 | 15 | constructor() { 16 | // Check if the acquireVsCodeApi function exists in the current development 17 | // context (i.e. VS Code development window or web browser) 18 | if (typeof acquireVsCodeApi === "function") { 19 | this.vsCodeApi = acquireVsCodeApi(); 20 | } 21 | } 22 | 23 | /** 24 | * Post a message (i.e. send arbitrary data) to the owner of the webview. 25 | * 26 | * @remarks When running webview code inside a web browser, postMessage will instead 27 | * log the given message to the console. 28 | * 29 | * @param message Abitrary data (must be JSON serializable) to send to the extension context. 30 | */ 31 | public postMessage(message: unknown) { 32 | if (this.vsCodeApi) { 33 | this.vsCodeApi.postMessage(message); 34 | } else { 35 | console.log(`VS Code API not available, raw message: ${message}`); 36 | } 37 | } 38 | } 39 | 40 | // Exports class singleton to prevent multiple invocations of acquireVsCodeApi. 41 | export const vscode = new VSCodeAPIWrapper(); 42 | -------------------------------------------------------------------------------- /src/webviewUtils.ts: -------------------------------------------------------------------------------- 1 | import { Uri, Webview } from "vscode"; 2 | 3 | export function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) { 4 | return webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList)); 5 | } 6 | 7 | export function getNonce() { 8 | let text = ""; 9 | const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 10 | for (let i = 0; i < 32; i++) { 11 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 12 | } 13 | return text; 14 | } 15 | -------------------------------------------------------------------------------- /test/Test Plan.md: -------------------------------------------------------------------------------- 1 | # Test platform 2 | 3 | ## Windows 10/maxOS/Linux 4 | 5 | # Scenarios 6 | 7 | ## Basic 8 | 1. Open .\resources\eclipse\simple-app. After the language server is initialized, check the status bar icon is :thumbsup:, and the problems view has two errors. 9 | 2. Select Foo.java file, invoke `class` code snippet to generate code as below and the problem view error number is reduced to 1. 10 | 11 | ```java 12 | package app; 13 | 14 | /** 15 | * Foo 16 | */ 17 | public class Foo { 18 | 19 | 20 | } 21 | ``` 22 | 3. Click the remain error in the diagnostic window, the code action icon should pop up both on the problems view and the side bar of the editor. Select the code action of `Create method 'call()' in type 'Foo'` to fix the error. 23 | 4. Save all the files, and invoke VSCode command "Java: Force Java compilation". There should be no errors. 24 | 5. Typing the following file of code into the App.main method, the completion should work for File and there should be two errors in the problem view. 25 | ```java 26 | File f = new File("demo.txt"); 27 | ``` 28 | 6. Invoke the context menu command `Source Action...` ==> `Organize Imports`, there should be only one warning remains in the problem view. 29 | 30 | ## Maven 31 | 1. Open .\resources\maven\salut. After the language server is initialized, check the status bar icon is :thumbsup:, and the problems views has several warnings but without errors. 32 | 2. Editing experience is correctly working including diagnostics, code completion and code actions. 33 | 34 | ## Maven - Multimodule 35 | 1. Open .\resources\maven\multimodule. After the language server is initialized, check the status bar icon is :thumbsup:, and there should be no errors/warning in the problems view. 36 | 2. Open Foo.java file, make sure the editing experience is correctly working including diagnostics, code completion and code action on both modules. 37 | - module1\Foo.java 38 | - module2\Foo.java 39 | 40 | ## Gradle 41 | 1. Open .\resources\gradle\simple-gradle. After the language server is initialized, check the status bar icon is :thumbsup:, and there should be no errors/problems in the problems view. 42 | 2. Open Foo.java file, make sure the editing experience is correctly working including diagnostics, code completion and code action 43 | 44 | ## Maven - Java 11 45 | 1. Install JDK 11, and change the VSCode java.home to the JDK 11 path. 46 | 2. Open .\resources\maven\salut-java11. After the language server is initialized, check the status bar icon is :thumbsup:, and there should be no errors/problems in the problems view. 47 | 3. Open Bar.java, make sure the editing experience is correctly working including diagnostics, code completion and code action 48 | 49 | ## Gradle - Java 11 50 | 1. Install JDK 11. 51 | 2. Open .\resources\gradle\gradle-11. After the language server is initialized, check the status bar icon is :thumbsup:, and there should be no errors/problems in the problems view. 52 | 2. Open Foo.java file, make sure the editing experience is correctly working including diagnostics, code completion and code action. 53 | 54 | ## Single file 55 | 1. Open/Create an empty folder 56 | 2. Add a new Java file, name it Test.java. Check the language server is initialized, and the status bar icon is :thumbsup: after that. 57 | 3. Type code snippet `class` to generate the class body, type `main` to generate the main methods. 58 | 4. In the Test.java file, make sure the editing experience is correctly working including diagnostics, code completion and code action. 59 | -------------------------------------------------------------------------------- /test/common.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | export namespace constants { 4 | export const projectFsPath: string = path.join(__dirname, '..', '..', 'test-temp'); 5 | } -------------------------------------------------------------------------------- /test/lightweight-mode-suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import * as vscode from 'vscode'; 3 | import { extensions } from 'vscode'; 4 | import { Commands } from '../../src/commands'; 5 | 6 | suite('Java Language Extension - LightWeight', () => { 7 | 8 | suiteSetup(async function() { 9 | await extensions.getExtension('redhat.java').activate(); 10 | }); 11 | 12 | test('should register syntax-only java commands', () => { 13 | return vscode.commands.getCommands(true).then((commands) => 14 | { 15 | const JAVA_COMMANDS = [ 16 | Commands.EXECUTE_WORKSPACE_COMMAND, 17 | Commands.OPEN_SERVER_LOG, 18 | Commands.OPEN_SERVER_STDOUT_LOG, 19 | Commands.OPEN_SERVER_STDERR_LOG, 20 | Commands.OPEN_CLIENT_LOG, 21 | Commands.OPEN_LOGS, 22 | Commands.OPEN_FORMATTER, 23 | Commands.CLEAN_WORKSPACE, 24 | Commands.SWITCH_SERVER_MODE, 25 | Commands.OPEN_FILE, 26 | Commands.CLEAN_SHARED_INDEXES, 27 | Commands.RESTART_LANGUAGE_SERVER, 28 | Commands.FILESEXPLORER_ONPASTE, 29 | Commands.CHANGE_JAVA_SEARCH_SCOPE 30 | ].sort(); 31 | const foundJavaCommands = commands.filter((value) => { 32 | return JAVA_COMMANDS.indexOf(value)>=0 || value.startsWith('java.'); 33 | }).sort(); 34 | assert.deepEqual(foundJavaCommands, JAVA_COMMANDS, `Some Java commands are not registered properly or a new command is not added to the test.\nActual: ${foundJavaCommands}\nExpected: ${JAVA_COMMANDS}`); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/lightweight-mode-suite/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | import * as path from 'path'; 14 | import * as Mocha from 'mocha'; 15 | import * as glob from 'glob'; 16 | 17 | export function run(testsRoot: string): Promise { 18 | const mocha = new Mocha({ 19 | ui: 'tdd', 20 | useColors: true, 21 | timeout: 1 * 60 * 1000, /* ms*/ 22 | }); 23 | 24 | return new Promise((c, e) => { 25 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 26 | if (err) { 27 | return e(err); 28 | } 29 | 30 | // Add files to the test suite 31 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 32 | 33 | try { 34 | // Run the mocha test 35 | mocha.run(failures => { 36 | if (failures > 0) { 37 | e(new Error(`${failures} tests failed.`)); 38 | } else { 39 | c(); 40 | } 41 | }); 42 | } catch (err) { 43 | e(err); 44 | } 45 | }); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /test/resources/packageExample.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-java-extension-example", 3 | "displayName": "vscode-java-extension-example", 4 | "description": "Example package to extend vscode java extension", 5 | "version": "0.0.1", 6 | "engines": { 7 | "vscode": "^1.14.0" 8 | }, 9 | "categories": [], 10 | "activationEvents": [ 11 | "onLanguage:java" 12 | ], 13 | "main": "./out/src/extension", 14 | "contributes": { 15 | "javaExtensions": [ 16 | "./bin/java.extend.jar" 17 | ], 18 | "javaBuildFilePatterns": [ 19 | "^pom.xml$", ".*\\.gradle(\\.kts)?$" 20 | ] 21 | }, 22 | "extensionDependencies": [ 23 | "redhat.java" 24 | ] 25 | } -------------------------------------------------------------------------------- /test/resources/projects/eclipse/simple-app/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/resources/projects/eclipse/simple-app/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | simple-app 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | -------------------------------------------------------------------------------- /test/resources/projects/eclipse/simple-app/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 4 | org.eclipse.jdt.core.compiler.compliance=1.8 5 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 6 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 7 | org.eclipse.jdt.core.compiler.source=1.8 8 | -------------------------------------------------------------------------------- /test/resources/projects/eclipse/simple-app/src/app/App.java: -------------------------------------------------------------------------------- 1 | package app; 2 | 3 | public class App { 4 | public static void main(String[] args) throws Exception { 5 | System.out.println("Hello Java"); 6 | } 7 | } -------------------------------------------------------------------------------- /test/resources/projects/eclipse/simple-app/src/app/Bar.java: -------------------------------------------------------------------------------- 1 | package app; 2 | 3 | /** 4 | * Bar 5 | */ 6 | public class Bar { 7 | 8 | public void name() { 9 | Foo f = new Foo(); 10 | f.call(); 11 | } 12 | } -------------------------------------------------------------------------------- /test/resources/projects/eclipse/simple-app/src/app/Foo.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/test/resources/projects/eclipse/simple-app/src/app/Foo.java -------------------------------------------------------------------------------- /test/resources/projects/gradle/gradle-11/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * This is a general purpose Gradle build. 5 | * Learn how to create Gradle builds at https://guides.gradle.org/creating-new-gradle-builds/ 6 | */ 7 | plugins { 8 | id 'java' 9 | } 10 | 11 | sourceCompatibility = '11' 12 | targetCompatibility = '11' 13 | version = '0.0.1-SNAPSHOT' 14 | -------------------------------------------------------------------------------- /test/resources/projects/gradle/gradle-11/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/test/resources/projects/gradle/gradle-11/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /test/resources/projects/gradle/gradle-11/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /test/resources/projects/gradle/gradle-11/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /test/resources/projects/gradle/gradle-11/settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user guide at https://docs.gradle.org/4.10.2/userguide/multi_project_builds.html 8 | */ 9 | 10 | rootProject.name = 'gradle-11' 11 | -------------------------------------------------------------------------------- /test/resources/projects/gradle/gradle-11/src/main/java/foo/bar/Foo.java: -------------------------------------------------------------------------------- 1 | package foo.bar; 2 | 3 | /** 4 | * It's a Foo class 5 | */ 6 | public class Foo { 7 | 8 | /** 9 | * It's a Bar interface 10 | */ 11 | interface Bar { 12 | void something(Foo i, Bar j); 13 | } 14 | 15 | public static void main(String[] args) { 16 | Bar bar = (var i, var j) -> System.out.println(i.bar() + j.toString()); 17 | System.out.print(bar); 18 | } 19 | 20 | public String bar() { 21 | return toString(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/resources/projects/gradle/simple-gradle/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | 4 | sourceSets { 5 | main.java.srcDirs = ['src/main/java'] 6 | } -------------------------------------------------------------------------------- /test/resources/projects/gradle/simple-gradle/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/test/resources/projects/gradle/simple-gradle/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /test/resources/projects/gradle/simple-gradle/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /test/resources/projects/gradle/simple-gradle/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /test/resources/projects/gradle/simple-gradle/settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user guide at https://docs.gradle.org/4.10.2/userguide/multi_project_builds.html 8 | */ 9 | 10 | rootProject.name = 'simple-gradle' 11 | -------------------------------------------------------------------------------- /test/resources/projects/gradle/simple-gradle/src/main/java/foo/bar/Foo.java: -------------------------------------------------------------------------------- 1 | package foo.bar; 2 | 3 | /** 4 | * It's a Foo class 5 | */ 6 | public class Foo { 7 | 8 | /** 9 | * It's a Bar interface 10 | */ 11 | interface Bar { 12 | void something(Foo i, Bar j); 13 | } 14 | 15 | public static void main(String[] args) { 16 | Bar bar = (Foo i, Bar j) -> System.out.println(i.bar() + j.toString()); 17 | System.out.print(bar); 18 | } 19 | 20 | public String bar() { 21 | return toString(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/resources/projects/maven/multimodule/module1/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | foo.bar 4 | module1 5 | 0.0.1-SNAPSHOT 6 | jar 7 | 8 | foo.bar 9 | multimodule 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | 14 | 15 | maven-compiler-plugin 16 | 3.8.0 17 | 18 | 1.8 19 | 1.8 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/resources/projects/maven/multimodule/module1/src/main/java/module1/Foo.java: -------------------------------------------------------------------------------- 1 | package module1; 2 | 3 | /** 4 | * Foo 5 | */ 6 | public class Foo { 7 | 8 | 9 | } -------------------------------------------------------------------------------- /test/resources/projects/maven/multimodule/module2/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | foo.bar 4 | module2 5 | 0.0.1-SNAPSHOT 6 | jar 7 | 8 | foo.bar 9 | multimodule 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | 14 | 15 | maven-compiler-plugin 16 | 3.7.0 17 | 18 | 1.8 19 | 1.8 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/resources/projects/maven/multimodule/module2/src/main/java/module2/Foo.java: -------------------------------------------------------------------------------- 1 | package module2; 2 | 3 | /** 4 | * Foo 5 | */ 6 | public class Foo { 7 | 8 | 9 | } -------------------------------------------------------------------------------- /test/resources/projects/maven/multimodule/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | foo.bar 4 | multimodule 5 | 0.0.1-SNAPSHOT 6 | pom 7 | 8 | module1 9 | module2 10 | 11 | 12 | 13 | 14 | maven-compiler-plugin 15 | 3.8.0 16 | 17 | 1.8 18 | 1.8 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/resources/projects/maven/salut-java11/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | foo.bar 5 | salut-java11 6 | 0.0.1-SNAPSHOT 7 | 8 | 9 | 10 | maven-compiler-plugin 11 | 3.8.0 12 | 13 | 11 14 | 15 | 16 | 17 | 18 | 19 | 20 | org.apache.commons 21 | commons-lang3 22 | 3.5 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/resources/projects/maven/salut-java11/src/main/java/org/sample/Bar.java: -------------------------------------------------------------------------------- 1 | package org.sample; 2 | 3 | /** 4 | * This is Bar 5 | */ 6 | public class Bar { 7 | 8 | public static void main(String[] args) { 9 | var foo = "Hello world! from "+Bar.class; 10 | System.out.print( foo ); 11 | } 12 | 13 | public static interface MyInterface { 14 | 15 | void foo(); 16 | } 17 | 18 | public static class MyClass { 19 | 20 | void bar() {} 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/resources/projects/maven/salut/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | foo.bar 5 | salut 6 | 0.0.1-SNAPSHOT 7 | 8 | 9 | 10 | maven-compiler-plugin 11 | 3.5.1 12 | 13 | 1.7 14 | 1.7 15 | 16 | 17 | 18 | 19 | 20 | 21 | org.apache.commons 22 | commons-lang3 23 | 3.5 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/resources/projects/maven/salut/src/main/java/java/Foo.java: -------------------------------------------------------------------------------- 1 | package java; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | /** 6 | * This is foo 7 | */ 8 | public class Foo { 9 | 10 | public static void main(String[] args) { 11 | System.out.print( StringUtils.capitalize("Hello world! from "+Foo.class)); 12 | } 13 | 14 | public void linkedFromFoo2() { 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /test/resources/projects/maven/salut/src/main/java/java/Foo2.java: -------------------------------------------------------------------------------- 1 | package java; 2 | 3 | import java.io.IOException; 4 | 5 | import org.apache.commons.lang3.AnnotationUtils; 6 | 7 | public class Foo2 { 8 | 9 | 10 | /** 11 | * {@value #mySimpleString} is a simple String 12 | */ 13 | public static final String mySimpleString = 14 | "SimpleStringData"; 15 | 16 | /** 17 | * {@link #newMethodBeingLinkedToo} 18 | */ 19 | public void javadocLinkToMethodInClass() { 20 | 21 | } 22 | 23 | private void newMethodBeingLinkedToo() { 24 | 25 | } 26 | 27 | /** 28 | * {@link Foo#linkedFromFoo2()} 29 | */ 30 | private void javadocLinkToMethodInOtherClass() { 31 | 32 | } 33 | 34 | /** 35 | * This Javadoc contains a link to 36 | * {@link #newMethodBeingLinkedToo} 37 | * @see Online docs for java 38 | * @param someString the string to enter 39 | * @since 0.0.1 40 | * @version 0.0.1 41 | * @author jpinkney 42 | * @return String 43 | * @throws IOException 44 | */ 45 | private String javadocLink(String someString) throws IOException { 46 | return null; 47 | } 48 | 49 | /** 50 | * This link doesnt work {@link LinkToSomethingNotFound} 51 | */ 52 | private void linkDoesNotExist() { 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /test/resources/projects/maven/salut/src/main/java/java/Foo3.java: -------------------------------------------------------------------------------- 1 | package java; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import org.apache.commons.lang3.StringUtils; 7 | 8 | public class Foo3 { 9 | 10 | private Set properties; 11 | 12 | public Foo3() { 13 | this.properties = new HashSet(); 14 | } 15 | 16 | public void localVariable() { 17 | StringUtils util = new StringUtils(); 18 | System.out.println(this.properties); 19 | System.out.println(util); 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return "Foo3 [properties=" + properties + "]"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/resources/projects/maven/salut/src/main/java/org/sample/Bar.java: -------------------------------------------------------------------------------- 1 | package org.sample; 2 | 3 | /** 4 | * This is Bar 5 | */ 6 | public class Bar { 7 | 8 | public static void main(String[] args) { 9 | System.out.print( "Hello world! from "+Bar.class); 10 | } 11 | 12 | public static interface MyInterface { 13 | 14 | void foo(); 15 | } 16 | 17 | public static class MyClass { 18 | 19 | void bar() {} 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/resources/projects/maven/salut/src/main/java/org/sample/TestJavadoc.java: -------------------------------------------------------------------------------- 1 | package org.sample; 2 | import org.apache.commons.lang3.text.WordUtils; 3 | public class TestJavadoc { 4 | 5 | private String foo() { 6 | Inner inner = new Inner(); 7 | return inner.test; 8 | } 9 | 10 | public class Inner { 11 | /** Test */ 12 | public String test; 13 | } 14 | 15 | /** 16 | * Uses {@link WordUtils} 17 | */ 18 | public void commonsLang() { 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /test/resources/projects/maven/salut/src/main/resources/test.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-java/fdca5e1a4911a15ee399ab900c45f71049172050/test/resources/projects/maven/salut/src/main/resources/test.properties -------------------------------------------------------------------------------- /test/resources/projects/singlefile/Single.java: -------------------------------------------------------------------------------- 1 | public class Single { 2 | public static void main(String[] args) 3 | { 4 | String s = "1"; 5 | System.out.println("Hello World!" + s); 6 | } 7 | } -------------------------------------------------------------------------------- /test/resources/projects/singlefile/WithError.java: -------------------------------------------------------------------------------- 1 | public class WrongName { 2 | public static void main(String[] args) 3 | { 4 | String s = "1"; 5 | System.out.println("Hello World!" + s); 6 | } -------------------------------------------------------------------------------- /test/runtest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as fse from 'fs-extra'; 3 | import { runTests } from '@vscode/test-electron'; 4 | 5 | async function main() { 6 | const testProjectOriginPath: string = path.join(__dirname, '..', '..', 'test', 'resources', 'projects', 'maven', 'salut'); 7 | const testProjectPath: string = path.join(__dirname, '..', '..', 'test-temp'); 8 | const settingsJsonPath: string = path.join(testProjectPath, '.vscode', 'settings.json'); 9 | try { 10 | // The folder containing the Extension Manifest package.json 11 | // Passed to `--extensionDevelopmentPath` 12 | const extensionDevelopmentPath = path.resolve(__dirname, '../..'); 13 | 14 | // run tests for standard mode 15 | await fse.copy(testProjectOriginPath, testProjectPath); 16 | await fse.ensureDir(path.join(testProjectPath, '.vscode')); 17 | /* eslint-disable @typescript-eslint/naming-convention */ 18 | await fse.writeJSON(settingsJsonPath, { 19 | "java.server.launchMode": "Standard", 20 | "java.configuration.updateBuildConfiguration": "automatic" 21 | }); 22 | /* eslint-enable @typescript-eslint/naming-convention */ 23 | 24 | await runTests({ 25 | extensionDevelopmentPath, 26 | extensionTestsPath: path.resolve(__dirname, './standard-mode-suite'), 27 | launchArgs: [ 28 | testProjectPath, 29 | '--disable-extensions', 30 | '--disable-workspace-trust' 31 | ] 32 | }); 33 | 34 | // run tests for lightweight mode 35 | console.log("setup settings.json for lightweight mode..."); 36 | /* eslint-disable @typescript-eslint/naming-convention */ 37 | await fse.writeJSON(settingsJsonPath, { 38 | "java.server.launchMode": "LightWeight", 39 | }); 40 | /* eslint-enable @typescript-eslint/naming-convention */ 41 | 42 | console.log("running lightweight cases..."); 43 | await runTests({ 44 | extensionDevelopmentPath, 45 | extensionTestsPath: path.resolve(__dirname, './lightweight-mode-suite'), 46 | launchArgs: [ 47 | testProjectPath, 48 | '--disable-extensions', 49 | '--disable-workspace-trust' 50 | ] 51 | }); 52 | } catch (err) { 53 | console.error(`Failed to run tests: ${err}`); 54 | process.exit(1); 55 | } finally { 56 | await fse.remove(testProjectPath); 57 | } 58 | } 59 | 60 | main(); 61 | -------------------------------------------------------------------------------- /test/standard-mode-suite/codeActionProvider.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as assert from 'assert'; 4 | import * as path from 'path'; 5 | import * as sinon from 'sinon'; 6 | import { CodeAction, CodeActionContext, CodeActionKind, CodeActionTriggerKind, DiagnosticSeverity, ExtensionContext, Position, Range, Uri, window } from "vscode"; 7 | import { Commands } from '../../src/commands'; 8 | import { GradleCodeActionProvider } from '../../src/gradle/gradleCodeActionProvider'; 9 | import { PomCodeActionProvider } from "../../src/pom/pomCodeActionProvider"; 10 | 11 | // tslint:disable: only-arrow-functions 12 | const mavenProjectFsPath: string = path.join(__dirname, '..', '..', '..', 'test', 'resources', 'projects', 'maven', 'salut'); 13 | const gradleProjectFsPath: string = path.join(__dirname, '..', '..', '..', 'test', 'resources', 'projects', 'gradle', 'simple-gradle'); 14 | 15 | suite('Code Action Provider Test', () => { 16 | 17 | test('Provide reload project action on demand for pom file', async function () { 18 | const provider = sinon.createStubInstance(PomCodeActionProvider); 19 | provider.provideCodeActions.restore(); 20 | provider.getNewTextIndentation.restore(); 21 | provider.collectCodeActions.restore(); 22 | 23 | const codeActionContext: CodeActionContext = { 24 | triggerKind: CodeActionTriggerKind.Invoke, 25 | diagnostics: [{ 26 | range: new Range(new Position(0, 0), new Position(0, 0)), 27 | message: 'The build file has been changed and may need reload to make it effective.', 28 | severity: DiagnosticSeverity.Information, 29 | source: 'Java' 30 | }], 31 | only: CodeActionKind.QuickFix 32 | }; 33 | const editor = await window.showTextDocument(Uri.file(path.join(mavenProjectFsPath, 'pom.xml'))); 34 | const codeActions = provider.provideCodeActions(editor.document, null, codeActionContext, null) as CodeAction[]; 35 | assert.equal(codeActions.length, 1); 36 | assert.equal(codeActions[0].command.command, Commands.CONFIGURATION_UPDATE); 37 | }); 38 | 39 | test('Provide reload project action on demand for gradle file', async function () { 40 | const provider = sinon.createStubInstance(GradleCodeActionProvider); 41 | provider.provideCodeActions.restore(); 42 | provider.provideGradleCodeActions.restore(); 43 | 44 | const codeActionContext: CodeActionContext = { 45 | triggerKind: CodeActionTriggerKind.Invoke, 46 | diagnostics: [{ 47 | range: new Range(new Position(0, 0), new Position(0, 0)), 48 | message: 'The build file has been changed and may need reload to make it effective.', 49 | severity: DiagnosticSeverity.Information, 50 | source: 'Java' 51 | }], 52 | only: CodeActionKind.QuickFix 53 | }; 54 | const editor = await window.showTextDocument(Uri.file(path.join(gradleProjectFsPath, 'build.gradle'))); 55 | const codeActions = (await provider.provideCodeActions(editor.document, null, codeActionContext, null)) as CodeAction[]; 56 | assert.equal(codeActions.length, 1); 57 | assert.equal(codeActions[0].command.command, Commands.CONFIGURATION_UPDATE); 58 | }); 59 | 60 | }); 61 | -------------------------------------------------------------------------------- /test/standard-mode-suite/gotoSuperImplementation.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as assert from 'assert'; 4 | import * as path from 'path'; 5 | import { commands, extensions, Position, Selection, TextDocument, Uri, window, workspace } from 'vscode'; 6 | import { Commands } from '../../src/commands'; 7 | import { ExtensionAPI } from '../../src/extension.api'; 8 | 9 | const projectFsPath: string = path.join(__dirname, '..', '..', '..', 'test', 'resources', 'projects', 'maven', 'salut'); 10 | const fileFsPath: string = path.join(projectFsPath, 'src', 'main', 'java', 'java', 'Foo3.java'); 11 | 12 | suite('Goto Super Implementation', () => { 13 | 14 | suiteSetup(async function() { 15 | const api: ExtensionAPI = await extensions.getExtension('redhat.java').activate(); 16 | await api.serverReady(); 17 | }); 18 | 19 | test('go to super implementation should work', async function () { 20 | const document: TextDocument = await workspace.openTextDocument(Uri.file(path.join(fileFsPath))); 21 | await window.showTextDocument(document); 22 | window.activeTextEditor.selection = new Selection( 23 | new Position(22, 18), 24 | new Position(22, 18), 25 | ); 26 | 27 | await commands.executeCommand(Commands.NAVIGATE_TO_SUPER_IMPLEMENTATION_COMMAND); 28 | 29 | assert.equal(path.basename(window.activeTextEditor.document.fileName), "Object.class"); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/standard-mode-suite/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | import * as path from 'path'; 14 | import * as Mocha from 'mocha'; 15 | import * as glob from 'glob'; 16 | 17 | export function run(testsRoot: string): Promise { 18 | const mocha = new Mocha({ 19 | ui: 'tdd', 20 | useColors: true, 21 | timeout: 1 * 60 * 1000, /* ms*/ 22 | }); 23 | 24 | return new Promise((c, e) => { 25 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 26 | if (err) { 27 | return e(err); 28 | } 29 | 30 | // Add files to the test suite 31 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 32 | 33 | try { 34 | // Run the mocha test 35 | mocha.run(failures => { 36 | if (failures > 0) { 37 | e(new Error(`${failures} tests failed.`)); 38 | } else { 39 | c(); 40 | } 41 | }); 42 | } catch (err) { 43 | e(err); 44 | } 45 | }); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /test/standard-mode-suite/projects.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as assert from 'assert'; 4 | import * as path from 'path'; 5 | import * as sinon from 'sinon'; 6 | import { Uri } from 'vscode'; 7 | import { TextDocumentIdentifier } from 'vscode-languageclient'; 8 | import { LanguageClient } from 'vscode-languageclient/node'; 9 | import { projectConfigurationUpdate } from '../../src/standardLanguageClientUtils'; 10 | 11 | const projectFsPath: string = path.join(__dirname, '..', '..', '..', 'test', 'resources', 'projects', 'maven', 'salut'); 12 | 13 | suite('Project Operations Test', () => { 14 | 15 | test('Reload project by test document identifier', async function () { 16 | const client = new LanguageClient('mock client', null, null); 17 | const sendNotificationStub = sinon.stub(client, 'sendNotification'); 18 | 19 | const pomPath = path.join(projectFsPath, 'pom.xml'); 20 | const identifier: TextDocumentIdentifier = { 21 | uri: Uri.file(pomPath).toString(), 22 | }; 23 | 24 | await projectConfigurationUpdate(client, identifier); 25 | 26 | assert.ok(sendNotificationStub.calledOnce); 27 | }); 28 | 29 | test('Reload project by Uri instance', async function () { 30 | const client = new LanguageClient('mock client', null, null); 31 | const sendNotificationStub = sinon.stub(client, 'sendNotification'); 32 | 33 | const pomPath = path.join(projectFsPath, 'pom.xml'); 34 | 35 | await projectConfigurationUpdate(client, Uri.file(pomPath)); 36 | 37 | assert.ok(sendNotificationStub.calledOnce); 38 | }); 39 | 40 | test('Reload project by Uri array', async function () { 41 | const client = new LanguageClient('mock client', null, null); 42 | const sendNotificationStub = sinon.stub(client, 'sendNotification'); 43 | 44 | const pomPath = path.join(projectFsPath, 'pom.xml'); 45 | 46 | await projectConfigurationUpdate(client, [Uri.file(pomPath)]); 47 | 48 | assert.ok(sendNotificationStub.calledOnce); 49 | }); 50 | 51 | }); 52 | -------------------------------------------------------------------------------- /test/standard-mode-suite/rename.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as path from 'path'; 4 | import * as fse from "fs-extra"; 5 | import { Uri, extensions, TextDocument, workspace, WorkspaceEdit } from 'vscode'; 6 | import { constants } from '../common'; 7 | 8 | const originFilePath: string = path.join(constants.projectFsPath, 'src', 'main', 'java', 'java', 'Foo.java'); 9 | const newFilePath: string = path.join(constants.projectFsPath, 'src', 'main', 'java', 'java', 'Foo1.java'); 10 | 11 | // Rename refactoring will pop up a dialog for confirm, didn't find a way to bypass it. So skip this test case. 12 | suite.skip('Rename tests', () => { 13 | 14 | suiteSetup(async function() { 15 | await extensions.getExtension('redhat.java').activate(); 16 | }); 17 | 18 | test('rename on file will update the class name', async function () { 19 | const workspaceEdit: WorkspaceEdit = new WorkspaceEdit(); 20 | workspaceEdit.renameFile(Uri.file(originFilePath), Uri.file(newFilePath)); 21 | await workspace.applyEdit(workspaceEdit); 22 | 23 | const document: TextDocument = await workspace.openTextDocument(Uri.file(path.join(newFilePath))); 24 | // wait for the extension to update the class name, 25 | // if it's not updated, this case will fail on timeout - 60s. 26 | while (true) { 27 | await sleep(5 * 1000 /* ms */); 28 | if (document.getText().includes("public class Foo1")) { 29 | break; 30 | } 31 | } 32 | }); 33 | 34 | suiteTeardown(async function() { 35 | // revert the rename changes 36 | if (await fse.pathExists(newFilePath)) { 37 | const workspaceEdit: WorkspaceEdit = new WorkspaceEdit(); 38 | workspaceEdit.renameFile(Uri.file(newFilePath), Uri.file(originFilePath)); 39 | await workspace.applyEdit(workspaceEdit); 40 | 41 | const document: TextDocument = await workspace.openTextDocument(Uri.file(path.join(originFilePath))); 42 | while (true) { 43 | await sleep(5 * 1000 /* ms */); 44 | if (document.getText().includes("public class Foo")) { 45 | break; 46 | } 47 | } 48 | } 49 | }); 50 | }); 51 | 52 | async function sleep(ms: number): Promise { 53 | return new Promise(resolve => { 54 | setTimeout(resolve, ms); 55 | }); 56 | } -------------------------------------------------------------------------------- /test/standard-mode-suite/snippetCompletionProvider.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { beautifyDocument } from '../../src/snippetCompletionProvider'; 3 | import { MarkdownString } from 'vscode'; 4 | 5 | suite('Snippet Completion Provider', () => { 6 | 7 | test('should render document correctly', () => { 8 | // /* eslint-disable prefer-arrow-callback, prefer-arrow/prefer-arrow-functions */:disable: prefer-template 9 | const raw = "for (${1:int} ${2:i} = ${3:0}; ${2:i} < ${4:max}; ${2:i}++) {\n" + "\t$0\n" + "}"; 10 | const markdownString: MarkdownString = beautifyDocument(raw); 11 | const expected: string = "\n```java\n" + "for (int i = 0; i < max; i++) {\n" + "\t\n" + "}\n" + "```\n"; 12 | assert.equal(markdownString.value, expected); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/standard-mode-suite/utils.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as assert from 'assert'; 4 | import { getJavaConfiguration, getBuildFilePatterns, getInclusionPatternsFromNegatedExclusion, getExclusionGlob, convertToGlob } from '../../src/utils'; 5 | import { WorkspaceConfiguration } from 'vscode'; 6 | import { listJdks } from '../../src/jdkUtils'; 7 | import { platform } from 'os'; 8 | 9 | let exclusion: string[]; 10 | let isMavenImporterEnabled: boolean; 11 | let isGradleImporterEnabled: boolean; 12 | 13 | const config: WorkspaceConfiguration = getJavaConfiguration(); 14 | const IMPORT_EXCLUSION: string = "import.exclusions"; 15 | const IMPORT_MAVEN_ENABLED: string = "import.maven.enabled"; 16 | const IMPORT_GRADLE_ENABLED: string = "import.gradle.enabled"; 17 | 18 | suite('Utils Test', () => { 19 | 20 | suiteSetup(async function() { 21 | exclusion = config.get(IMPORT_EXCLUSION, []); 22 | isMavenImporterEnabled = config.get(IMPORT_MAVEN_ENABLED, true); 23 | isGradleImporterEnabled = config.get(IMPORT_GRADLE_ENABLED, true); 24 | }); 25 | 26 | test('getBuildFilePatterns() - Maven importer is enabled', async function () { 27 | await config.update(IMPORT_MAVEN_ENABLED, true); 28 | await config.update(IMPORT_GRADLE_ENABLED, false); 29 | 30 | const result: string[] = getBuildFilePatterns(); 31 | 32 | assert.deepEqual(result, ["**/pom.xml"]); 33 | }); 34 | 35 | test('getBuildFilePatterns() - all importers are enabled', async function () { 36 | await config.update(IMPORT_MAVEN_ENABLED, true); 37 | await config.update(IMPORT_GRADLE_ENABLED, true); 38 | 39 | const result: string[] = getBuildFilePatterns(); 40 | 41 | assert.deepEqual(result, ["**/pom.xml", "**/*.gradle", "**/*.gradle.kts"]); 42 | }); 43 | 44 | test('getBuildFilePatterns() - no importers is enabled', async function () { 45 | await config.update(IMPORT_MAVEN_ENABLED, false); 46 | await config.update(IMPORT_GRADLE_ENABLED, false); 47 | 48 | const result: string[] = getBuildFilePatterns(); 49 | 50 | assert.deepEqual(result, []); 51 | }); 52 | 53 | test('getInclusionPatternsFromNegatedExclusion() - no negated exclusions', async function () { 54 | await config.update(IMPORT_EXCLUSION, [ 55 | "**/node_modules/**", 56 | "**/.metadata/**", 57 | "**/archetype-resources/**", 58 | "**/META-INF/maven/**" 59 | ]); 60 | 61 | const result: string[] = getInclusionPatternsFromNegatedExclusion(); 62 | 63 | assert.deepEqual(result, []); 64 | }); 65 | 66 | test('getInclusionPatternsFromNegatedExclusion() - has negated exclusions', async function () { 67 | await config.update(IMPORT_EXCLUSION, [ 68 | "**/node_modules/**", 69 | "!**/node_modules/test/**", 70 | "**/.metadata/**", 71 | "**/archetype-resources/**", 72 | "**/META-INF/maven/**" 73 | ]); 74 | 75 | const result: string[] = getInclusionPatternsFromNegatedExclusion(); 76 | 77 | assert.deepEqual(result, ["**/node_modules/test/**"]); 78 | }); 79 | 80 | test('getExclusionGlob() - no negated exclusions', async function () { 81 | await config.update(IMPORT_EXCLUSION, [ 82 | "**/node_modules/**", 83 | "**/.metadata/**", 84 | "**/archetype-resources/**", 85 | "**/META-INF/maven/**" 86 | ]); 87 | 88 | const result: string = getExclusionGlob(); 89 | 90 | assert.equal(result, "{**/node_modules/**,**/.metadata/**,**/archetype-resources/**,**/META-INF/maven/**}"); 91 | }); 92 | 93 | test('getExclusionGlob() - has negated exclusions', async function () { 94 | await config.update(IMPORT_EXCLUSION, [ 95 | "**/node_modules/**", 96 | "!**/node_modules/test/**", 97 | "**/.metadata/**", 98 | "**/archetype-resources/**", 99 | "**/META-INF/maven/**" 100 | ]); 101 | 102 | const result: string = getExclusionGlob(); 103 | 104 | assert.equal(result, "{**/node_modules/**,**/.metadata/**,**/archetype-resources/**,**/META-INF/maven/**}"); 105 | }); 106 | 107 | test('convertToGlob() - no file patterns', async function () { 108 | const result: string = convertToGlob([]); 109 | assert.equal(result, ""); 110 | }); 111 | 112 | test('convertToGlob() - no base patterns', async function () { 113 | const result: string = convertToGlob(["**/pom.xml", "**/build.gradle"]); 114 | assert.equal(result, "{**/pom.xml,**/build.gradle}"); 115 | }); 116 | 117 | test('convertToGlob() - has base patterns', async function () { 118 | const result: string = convertToGlob(["**/pom.xml"], ["**/node_modules/test/**"]); 119 | assert.equal(result, "{**/node_modules/test/**/**/pom.xml}"); 120 | }); 121 | 122 | test('listJdks() - no /usr as Java home on macOS', async function () { 123 | // Skip this test if it's not macOS. 124 | if (platform() !== "darwin") { 125 | this.skip(); 126 | } 127 | 128 | const jdks = await listJdks(); 129 | assert.ok(jdks.every(jdk => jdk.homedir !== "/usr")); 130 | }); 131 | 132 | teardown(async function() { 133 | await recover(); 134 | }); 135 | }); 136 | 137 | async function recover(): Promise { 138 | await config.update(IMPORT_EXCLUSION, exclusion); 139 | await config.update(IMPORT_MAVEN_ENABLED, isMavenImporterEnabled); 140 | await config.update(IMPORT_GRADLE_ENABLED, isGradleImporterEnabled); 141 | } 142 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "moduleResolution": "node", 5 | "outDir": "out", 6 | "sourceMap": true, 7 | "plugins": [ 8 | { 9 | "name": "eslint" 10 | } 11 | ], 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "lib": [ 5 | "es6" 6 | ], 7 | "module": "commonjs", 8 | }, 9 | "exclude": [ 10 | "node_modules", 11 | "server", 12 | ".vscode-test", 13 | "src/webview" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.webview.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "lib": [ 5 | "DOM" 6 | ], 7 | "module": "ES6", 8 | /* Enabled for React */ 9 | "jsx": "react-jsx", 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "skipLibCheck": true, 13 | }, 14 | "include": [ 15 | "src/webview" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | //@ts-check 5 | const ESLintWebpackPlugin = require('eslint-webpack-plugin'); 6 | const webpack = require('webpack'); 7 | const path = require('path'); 8 | 9 | /**@type {import('webpack').Configuration}*/ 10 | const config = { 11 | watchOptions: { 12 | ignored: /node_modules/ 13 | }, 14 | target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ 15 | node: { 16 | __dirname: false, 17 | __filename: false, 18 | }, 19 | entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 20 | output: { // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 21 | path: path.resolve(__dirname, 'dist'), 22 | filename: 'extension.js', 23 | libraryTarget: "commonjs2", 24 | devtoolModuleFilenameTemplate: "../[resource-path]", 25 | }, 26 | externals: { 27 | vscode: "commonjs vscode", // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ 28 | fsevents: "require('fsevents')", // https://github.com/yan-foto/electron-reload/issues/71#issuecomment-588988382 29 | }, 30 | devtool: 'source-map', 31 | resolve: { // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 32 | extensions: ['.ts', '.js'], 33 | }, 34 | module: { 35 | rules: [{ 36 | test: /\.ts$/, 37 | exclude: /node_modules/, 38 | use: [{ 39 | loader: 'ts-loader', 40 | }] 41 | }] 42 | }, 43 | plugins: [ 44 | new ESLintWebpackPlugin({ 45 | extensions: [ 46 | ".ts", 47 | ".js" 48 | ] 49 | }) 50 | ], 51 | infrastructureLogging: { 52 | level: 'log', 53 | }, 54 | } 55 | 56 | const configAssets = { 57 | name: 'assets', 58 | mode: 'none', 59 | entry: { 60 | changeSignature: './src/webview/changeSignature/index.tsx', 61 | }, 62 | module: { 63 | rules: [{ 64 | test: /\.ts(x?)$/, 65 | exclude: /node_modules/, 66 | loader: 'ts-loader', 67 | options: { 68 | configFile: 'tsconfig.webview.json' 69 | } 70 | }, { 71 | test: /\.(css)$/, 72 | use: [{ 73 | loader: 'style-loader' 74 | }, { 75 | loader: 'css-loader' 76 | }] 77 | }, { 78 | test: /\.(ttf)$/, 79 | type: 'asset/inline', 80 | }] 81 | }, 82 | output: { 83 | filename: '[name].js', 84 | path: path.resolve(__dirname, 'dist'), 85 | publicPath: '/', 86 | devtoolModuleFilenameTemplate: "../[resource-path]" 87 | }, 88 | plugins: [ 89 | new webpack.ProvidePlugin({ 90 | process: 'process/browser', 91 | }), 92 | ], 93 | devtool: 'source-map', 94 | resolve: { 95 | extensions: ['.js', '.ts', '.tsx'] 96 | } 97 | } 98 | module.exports = [config, configAssets]; 99 | -------------------------------------------------------------------------------- /webview-resources/button.css: -------------------------------------------------------------------------------- 1 | /* https://css-tricks.com/overriding-default-button-styles/ */ 2 | 3 | button { 4 | display: inline-block; 5 | border: none; 6 | padding: 12px 16px; 7 | margin: 0; 8 | text-decoration: none; 9 | background: var(--vscode-button-background); 10 | color: #ffffff; 11 | font-family: sans-serif; 12 | font-size: 14px; 13 | cursor: pointer; 14 | text-align: center; 15 | } 16 | 17 | button:hover, 18 | button:focus { 19 | background: var(--vscode-button-hoverBackground); 20 | } 21 | button:focus { 22 | outline: 1px solid var(--vscode-button-hoverBackground); 23 | /* outline-offset: -4px; */ 24 | } 25 | 26 | .center { 27 | text-align: center; 28 | margin: 0; 29 | position: absolute; 30 | top: 50%; 31 | left: 50%; 32 | -ms-transform: translate(-50%, -50%); 33 | transform: translate(-50%, -50%); 34 | } -------------------------------------------------------------------------------- /webview-resources/document.css: -------------------------------------------------------------------------------- 1 | .btn { 2 | border: 0; 3 | color: var(--vscode-button-foreground); 4 | background-color: var(--vscode-button-background); 5 | } 6 | 7 | .btn svg { 8 | fill: var(--vscode-button-foreground); 9 | } 10 | 11 | .btn:hover { 12 | background-color: var(--vscode-button-hoverBackground); 13 | } 14 | 15 | .floating-bottom-right { 16 | position: fixed; 17 | bottom: 1rem; 18 | right: 1rem; 19 | } -------------------------------------------------------------------------------- /webview-resources/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs2015.css 3 | */ 4 | /* 5 | * Visual Studio 2015 dark style 6 | * Author: Nicolas LLOBERA 7 | */ 8 | 9 | 10 | .hljs-keyword, 11 | .hljs-literal, 12 | .hljs-symbol, 13 | .hljs-name { 14 | color: #569CD6; 15 | } 16 | .hljs-link { 17 | color: #569CD6; 18 | text-decoration: underline; 19 | } 20 | 21 | .hljs-built_in, 22 | .hljs-type { 23 | color: #4EC9B0; 24 | } 25 | 26 | .hljs-number, 27 | .hljs-class { 28 | color: #B8D7A3; 29 | } 30 | 31 | .hljs-string, 32 | .hljs-meta-string { 33 | color: #D69D85; 34 | } 35 | 36 | .hljs-regexp, 37 | .hljs-template-tag { 38 | color: #9A5334; 39 | } 40 | 41 | .hljs-subst, 42 | .hljs-function, 43 | .hljs-title, 44 | .hljs-params, 45 | .hljs-formula { 46 | color: #DCDCDC; 47 | } 48 | 49 | .hljs-comment, 50 | .hljs-quote { 51 | color: #57A64A; 52 | font-style: italic; 53 | } 54 | 55 | .hljs-doctag { 56 | color: #608B4E; 57 | } 58 | 59 | .hljs-meta, 60 | .hljs-meta-keyword, 61 | .hljs-tag { 62 | color: #9B9B9B; 63 | } 64 | 65 | .hljs-variable, 66 | .hljs-template-variable { 67 | color: #BD63C5; 68 | } 69 | 70 | .hljs-attr, 71 | .hljs-attribute, 72 | .hljs-builtin-name { 73 | color: #9CDCFE; 74 | } 75 | 76 | .hljs-section { 77 | color: gold; 78 | } 79 | 80 | .hljs-emphasis { 81 | font-style: italic; 82 | } 83 | 84 | .hljs-strong { 85 | font-weight: bold; 86 | } 87 | 88 | /*.hljs-code { 89 | font-family:'Monospace'; 90 | }*/ 91 | 92 | .hljs-bullet, 93 | .hljs-selector-tag, 94 | .hljs-selector-id, 95 | .hljs-selector-class, 96 | .hljs-selector-attr, 97 | .hljs-selector-pseudo { 98 | color: #D7BA7D; 99 | } 100 | 101 | .hljs-addition { 102 | background-color: var(--vscode-diffEditor-insertedTextBackground, rgba(155, 185, 85, 0.2)); 103 | color: rgb(155, 185, 85); 104 | display: inline-block; 105 | width: 100%; 106 | } 107 | 108 | .hljs-deletion { 109 | background: var(--vscode-diffEditor-removedTextBackground, rgba(255, 0, 0, 0.2)); 110 | color: rgb(255, 0, 0); 111 | display: inline-block; 112 | width: 100%; 113 | } 114 | 115 | 116 | /* 117 | From https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs.css 118 | */ 119 | /* 120 | 121 | Visual Studio-like style based on original C# coloring by Jason Diamond 122 | 123 | */ 124 | 125 | .vscode-light .hljs-function, 126 | .vscode-light .hljs-params, 127 | .vscode-light .hljs-number, 128 | .vscode-light .hljs-class { 129 | color: inherit; 130 | } 131 | 132 | .vscode-light .hljs-comment, 133 | .vscode-light .hljs-quote, 134 | .vscode-light .hljs-number, 135 | .vscode-light .hljs-class, 136 | .vscode-light .hljs-variable { 137 | color: #008000; 138 | } 139 | 140 | .vscode-light .hljs-keyword, 141 | .vscode-light .hljs-selector-tag, 142 | .vscode-light .hljs-name, 143 | .vscode-light .hljs-tag { 144 | color: #00f; 145 | } 146 | 147 | .vscode-light .hljs-built_in, 148 | .vscode-light .hljs-builtin-name { 149 | color: #007acc; 150 | } 151 | 152 | .vscode-light .hljs-string, 153 | .vscode-light .hljs-section, 154 | .vscode-light .hljs-attribute, 155 | .vscode-light .hljs-literal, 156 | .vscode-light .hljs-template-tag, 157 | .vscode-light .hljs-template-variable, 158 | .vscode-light .hljs-type { 159 | color: #a31515; 160 | } 161 | 162 | .vscode-light .hljs-selector-attr, 163 | .vscode-light .hljs-selector-pseudo, 164 | .vscode-light .hljs-meta, 165 | .vscode-light .hljs-meta-keyword { 166 | color: #2b91af; 167 | } 168 | 169 | .vscode-light .hljs-title, 170 | .vscode-light .hljs-doctag { 171 | color: #808080; 172 | } 173 | 174 | .vscode-light .hljs-attr { 175 | color: #f00; 176 | } 177 | 178 | .vscode-light .hljs-symbol, 179 | .vscode-light .hljs-bullet, 180 | .vscode-light .hljs-link { 181 | color: #00b0e8; 182 | } 183 | 184 | 185 | .vscode-light .hljs-emphasis { 186 | font-style: italic; 187 | } 188 | 189 | .vscode-light .hljs-strong { 190 | font-weight: bold; 191 | } --------------------------------------------------------------------------------