├── .azure-pipelines ├── ci.yml ├── nightly.yml └── rc.yml ├── .github ├── dependabot.yml └── workflows │ ├── linux.yml │ ├── linuxUI.yml │ ├── macOS.yml │ ├── no-response.yml │ ├── windows.yml │ └── windowsUI.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── ThirdPartyNotices.txt ├── extension.bundle.ts ├── icons ├── dark │ ├── icon-link.svg │ └── icon-unlink.svg └── light │ ├── icon-link.svg │ └── icon-unlink.svg ├── images ├── create-project.png ├── export-jar.png ├── manage-dependencies.gif └── project-explorer.png ├── javaConfig.json ├── jdtls.ext ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── check_style.xml ├── com.microsoft.jdtls.ext.core │ ├── .classpath │ ├── .project │ ├── .settings │ │ ├── org.eclipse.core.resources.prefs │ │ ├── org.eclipse.jdt.core.prefs │ │ └── org.eclipse.m2e.core.prefs │ ├── META-INF │ │ └── MANIFEST.MF │ ├── build.properties │ ├── plugin.xml │ ├── pom.xml │ └── src │ │ ├── com │ │ └── microsoft │ │ │ └── jdtls │ │ │ └── ext │ │ │ └── core │ │ │ ├── CommandHandler.java │ │ │ ├── ExtUtils.java │ │ │ ├── JarFileContentProvider.java │ │ │ ├── JdtlsExtActivator.java │ │ │ ├── PackageCommand.java │ │ │ ├── PackageParams.java │ │ │ ├── ProjectCommand.java │ │ │ ├── model │ │ │ ├── ContainerNode.java │ │ │ ├── NodeKind.java │ │ │ ├── PackageNode.java │ │ │ ├── PackageRootNode.java │ │ │ ├── Trie.java │ │ │ └── TrieNode.java │ │ │ └── parser │ │ │ ├── JavaResourceVisitor.java │ │ │ ├── ResourceSet.java │ │ │ └── ResourceVisitor.java │ │ └── org │ │ └── eclipse │ │ └── jdt │ │ └── internal │ │ └── jarpackager │ │ └── JarPackageUtil.java ├── com.microsoft.jdtls.ext.target │ ├── com.microsoft.jdtls.ext.tp.target │ └── pom.xml ├── mvnw ├── mvnw.cmd └── pom.xml ├── logo.png ├── main.js ├── package-lock.json ├── package.json ├── package.nls.json ├── package.nls.zh-cn.json ├── package.nls.zh-tw.json ├── scripts ├── buildJdtlsExt.js └── prepare-nightly-build.js ├── src ├── ExperimentationService.ts ├── build.ts ├── commands.ts ├── constants.ts ├── contextManager.ts ├── controllers │ ├── libraryController.ts │ └── projectController.ts ├── explorerCommands │ ├── delete.ts │ ├── new.ts │ ├── rename.ts │ └── utility.ts ├── extension.ts ├── java │ ├── containerNodeData.ts │ ├── hierarchicalPackageNodeData.ts │ ├── jdtls.ts │ ├── nodeData.ts │ ├── packageRootNodeData.ts │ └── typeRootNodeData.ts ├── languageServerApi │ ├── LanguageServerMode.ts │ └── languageServerApiManager.ts ├── settings.ts ├── syncHandler.ts ├── tasks │ ├── build │ │ └── buildTaskProvider.ts │ └── buildArtifact │ │ ├── BuildArtifactTaskProvider.ts │ │ ├── GenerateJarExecutor.ts │ │ ├── IExportJarStepExecutor.ts │ │ ├── IStepMetadata.ts │ │ ├── ResolveJavaProjectExecutor.ts │ │ ├── ResolveMainClassExecutor.ts │ │ ├── migration │ │ ├── CodeActionProvider.ts │ │ ├── DiagnosticProvider.ts │ │ └── utils.ts │ │ └── utility.ts ├── utility.ts ├── utils │ └── Lock.ts └── views │ ├── DragAndDropController.ts │ ├── PrimaryTypeNode.ts │ ├── containerNode.ts │ ├── dataNode.ts │ ├── dependencyDataProvider.ts │ ├── dependencyExplorer.ts │ ├── documentSymbolNode.ts │ ├── explorerNode.ts │ ├── fileNode.ts │ ├── folderNode.ts │ ├── hierarchicalPackageNode.ts │ ├── hierarchicalPackageRootNode.ts │ ├── nodeCache │ ├── Trie.ts │ └── explorerNodeCache.ts │ ├── nodeFactory.ts │ ├── packageNode.ts │ ├── packageRootNode.ts │ ├── projectNode.ts │ └── workspaceNode.ts ├── templates └── invisible-project │ ├── .vscode │ └── settings.json │ ├── README.md │ └── src │ └── App.java ├── test ├── gradle-suite │ ├── index.ts │ └── projectView.test.ts ├── gradle │ ├── .vscode │ │ ├── launch.json │ │ └── settings.json │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── GradleTest.java ├── index.ts ├── invisible-suite │ ├── index.ts │ └── projectView.test.ts ├── invisible │ ├── .vscode │ │ └── settings.json │ ├── libSource │ │ └── simple.jar │ └── src │ │ └── App.java ├── java9 │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ ├── com │ │ │ └── microsoft │ │ │ │ └── app │ │ │ │ ├── App.java │ │ │ │ └── Launcher.java │ │ │ └── module-info.java │ │ └── test │ │ └── java │ │ └── com │ │ └── microsoft │ │ └── app │ │ └── AppTest.java ├── maven-suite │ ├── buildArtifact.test.ts │ ├── context.test.ts │ ├── index.ts │ └── projectView.test.ts ├── maven │ ├── .hidden │ ├── .vscode │ │ ├── launch.json │ │ ├── settings.json │ │ └── tasks.json │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ ├── com │ │ │ └── mycompany │ │ │ │ ├── app │ │ │ │ ├── App.java │ │ │ │ ├── AppToDelete.java │ │ │ │ ├── AppToRename.java │ │ │ │ └── package-info.java │ │ │ │ └── app1 │ │ │ │ └── App1.java │ │ │ └── module-info.java │ │ └── test │ │ └── java │ │ └── com │ │ └── mycompany │ │ └── app │ │ └── AppTest.java ├── multi-module-suite │ ├── index.ts │ └── projectView.test.ts ├── multi-module │ ├── .vscode │ │ └── settings.json │ ├── level1 │ │ └── pom.xml │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── fvclaus │ │ │ └── App.java │ │ └── test │ │ └── java │ │ └── fvclaus │ │ └── AppTest.java ├── multiple │ └── multiple-project.code-workspace ├── shared.ts ├── simple-suite │ ├── index.ts │ └── projectView.test.ts ├── simple │ ├── .classpath │ ├── .project │ ├── .vscode │ │ ├── launch.json │ │ └── settings.json │ └── src │ │ └── main │ │ └── java │ │ └── HelloWorld.java ├── suite │ ├── buildTask.test.ts │ ├── contextValue.test.ts │ ├── extension.test.ts │ └── index.ts ├── ui │ ├── command.test.ts │ └── index.ts └── util.ts ├── tsconfig.json ├── tslint.json └── webpack.config.js /.azure-pipelines/ci.yml: -------------------------------------------------------------------------------- 1 | name: $(Date:yyyyMMdd).$(Rev:r) 2 | variables: 3 | - name: Codeql.Enabled 4 | value: true 5 | resources: 6 | repositories: 7 | - repository: self 8 | type: git 9 | ref: refs/heads/main 10 | - repository: 1esPipelines 11 | type: git 12 | name: 1ESPipelineTemplates/1ESPipelineTemplates 13 | ref: refs/tags/release 14 | trigger: 15 | branches: 16 | include: 17 | - main 18 | extends: 19 | template: v1/1ES.Unofficial.PipelineTemplate.yml@1esPipelines 20 | parameters: 21 | pool: 22 | os: linux 23 | name: 1ES_JavaTooling_Pool 24 | image: 1ES_JavaTooling_Ubuntu-2004 25 | sdl: 26 | sourceAnalysisPool: 27 | name: 1ES_JavaTooling_Pool 28 | image: 1ES_JavaTooling_Windows_2022 29 | os: windows 30 | customBuildTags: 31 | - MigrationTooling-mseng-VSJava-9020-Tool 32 | stages: 33 | - stage: Build 34 | jobs: 35 | - job: Job_1 36 | displayName: CI 37 | templateContext: 38 | outputs: 39 | - output: pipelineArtifact 40 | artifactName: extension 41 | targetPath: $(Build.ArtifactStagingDirectory) 42 | displayName: "Publish Artifact: extension" 43 | steps: 44 | - checkout: self 45 | fetchTags: false 46 | - task: JavaToolInstaller@0 47 | displayName: Use Java 21 48 | inputs: 49 | versionSpec: "21" 50 | jdkArchitectureOption: x64 51 | jdkSourceOption: PreInstalled 52 | - task: NodeTool@0 53 | displayName: Use Node 20.x 54 | inputs: 55 | versionSpec: 20.x 56 | - task: Npm@1 57 | displayName: npm install 58 | inputs: 59 | verbose: true 60 | - task: Npm@1 61 | displayName: npm run build-server 62 | inputs: 63 | command: custom 64 | verbose: false 65 | customCommand: run build-server 66 | - task: Bash@3 67 | displayName: vsce package 68 | inputs: 69 | targetType: inline 70 | script: |- 71 | cd $(Build.SourcesDirectory) 72 | 73 | npx @vscode/vsce@latest package 74 | - task: CopyFiles@2 75 | displayName: "Copy Files to: $(Build.ArtifactStagingDirectory)" 76 | inputs: 77 | Contents: "*.vsix" 78 | TargetFolder: $(Build.ArtifactStagingDirectory) 79 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | linux: 11 | name: Linux 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 30 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Setup Build Environment 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 21 | sudo /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & 22 | sleep 3 23 | 24 | - name: Set up JDK 21 25 | uses: actions/setup-java@v1 26 | with: 27 | java-version: '21' 28 | 29 | - name: Setup Node.js environment 30 | uses: actions/setup-node@v2 31 | with: 32 | node-version: 20 33 | 34 | - name: Install Node.js modules 35 | run: npm install 36 | 37 | - name: Install VSCE 38 | run: npm install -g @vscode/vsce 39 | 40 | - name: Build OSGi bundle 41 | run: npm run build-server 42 | 43 | - name: Build VSIX file 44 | run: vsce package 45 | 46 | - name: Test extension 47 | run: DISPLAY=:99 npm test 48 | 49 | - name: Print language server Log if job failed 50 | if: ${{ failure() }} 51 | run: find $HOME/.config/Code/User/workspaceStorage/*/redhat.java/jdt_ws/.metadata/.log -print -exec cat '{}' \;; -------------------------------------------------------------------------------- /.github/workflows/linuxUI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | linuxUI: 11 | name: Linux-UI 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 30 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Setup Build Environment 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 21 | sudo /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & 22 | sleep 3 23 | 24 | - name: Set up JDK 21 25 | uses: actions/setup-java@v1 26 | with: 27 | java-version: '21' 28 | 29 | - name: Setup Node.js environment 30 | uses: actions/setup-node@v2 31 | with: 32 | node-version: 20 33 | 34 | - name: Install Node.js modules 35 | run: npm install 36 | 37 | - name: Install VSCE 38 | run: npm install -g vsce 39 | 40 | - name: Build OSGi bundle 41 | run: npm run build-server 42 | 43 | - name: Build VSIX file 44 | run: vsce package 45 | 46 | - name: UI Test 47 | continue-on-error: true 48 | id: test 49 | run: DISPLAY=:99 npm run test-ui 50 | 51 | - name: Retry UI Test 1 52 | continue-on-error: true 53 | if: steps.test.outcome=='failure' 54 | id: retry1 55 | run: | 56 | git reset --hard 57 | git clean -fd 58 | DISPLAY=:99 npm run test-ui 59 | 60 | - name: Retry UI Test 2 61 | continue-on-error: true 62 | if: steps.retry1.outcome=='failure' 63 | id: retry2 64 | run: | 65 | git reset --hard 66 | git clean -fd 67 | DISPLAY=:99 npm run test-ui 68 | 69 | - name: Set test status 70 | if: ${{ steps.test.outcome=='failure' && steps.retry1.outcome=='failure' && steps.retry2.outcome=='failure' }} 71 | run: | 72 | echo "Tests failed" 73 | exit 1 74 | 75 | - name: Print language server Log 76 | if: ${{ failure() }} 77 | run: find ./test-resources/settings/User/workspaceStorage/*/redhat.java/jdt_ws/.metadata/.log -print -exec cat '{}' \;; 78 | -------------------------------------------------------------------------------- /.github/workflows/macOS.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | darwin: 11 | name: macOS 12 | runs-on: macos-latest 13 | timeout-minutes: 30 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Set up JDK 21 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: '21' 21 | 22 | - name: Setup Node.js environment 23 | uses: actions/setup-node@v2 24 | with: 25 | node-version: 20 26 | 27 | - name: Install Node.js modules 28 | run: npm install 29 | 30 | - name: Install VSCE 31 | run: npm install -g @vscode/vsce 32 | 33 | - name: Build OSGi bundle 34 | run: npm run build-server 35 | 36 | - name: Build VSIX file 37 | run: vsce package 38 | 39 | - name: Test extension 40 | run: npm test 41 | 42 | - name: Print language server Log if job failed 43 | if: ${{ failure() }} 44 | run: find $HOME/Library/Application\ Support/Code/User/workspaceStorage/*/redhat.java/jdt_ws/.metadata/.log -print -exec cat '{}' \;; 45 | -------------------------------------------------------------------------------- /.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 | # every morning at 5:30 AM 11 | - cron: '30 5 * * *' 12 | 13 | jobs: 14 | noResponse: 15 | runs-on: ubuntu-latest 16 | permissions: 17 | issues: write 18 | steps: 19 | - uses: lee-dohm/no-response@9bb0a4b5e6a45046f00353d5de7d90fb8bd773bb 20 | with: 21 | token: ${{ github.token }} 22 | daysUntilClose: 14 23 | responseRequiredLabel: "need more info" 24 | closeComment: > 25 | 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. -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | windows: 11 | name: Windows 12 | runs-on: windows-latest 13 | timeout-minutes: 30 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Set up JDK 21 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: '21' 21 | 22 | - name: Setup Node.js environment 23 | uses: actions/setup-node@v2 24 | with: 25 | node-version: 20 26 | 27 | - name: Install Node.js modules 28 | run: npm install 29 | 30 | - name: Install VSCE 31 | run: npm install -g @vscode/vsce 32 | 33 | - name: Lint 34 | run: npm run tslint 35 | 36 | - name: Build OSGi bundle 37 | run: npm run build-server 38 | 39 | - name: Build VSIX file 40 | run: vsce package 41 | 42 | - name: Test extension 43 | run: npm test 44 | 45 | - name: Print language server Log if job failed 46 | if: ${{ failure() }} 47 | run: Get-ChildItem -Path $env:APPDATA/Code/User/workspaceStorage/*/redhat.java/jdt_ws/.metadata/.log | cat 48 | -------------------------------------------------------------------------------- /.github/workflows/windowsUI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | windowsUI: 11 | name: Windows-UI 12 | runs-on: windows-latest 13 | timeout-minutes: 30 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Set up JDK 21 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: '21' 21 | 22 | - name: Setup Node.js environment 23 | uses: actions/setup-node@v2 24 | with: 25 | node-version: 20 26 | 27 | - name: Install Node.js modules 28 | run: npm install 29 | 30 | - name: Install VSCE 31 | run: npm install -g vsce 32 | 33 | - name: Lint 34 | run: npm run tslint 35 | 36 | - name: Checkstyle 37 | working-directory: .\jdtls.ext 38 | run: .\mvnw.cmd checkstyle:check 39 | 40 | - name: Build OSGi bundle 41 | run: npm run build-server 42 | 43 | - name: Build VSIX file 44 | run: vsce package 45 | 46 | - name: UI Test 47 | continue-on-error: true 48 | id: test 49 | run: npm run test-ui 50 | 51 | - name: Retry UI Test 1 52 | continue-on-error: true 53 | if: steps.test.outcome=='failure' 54 | id: retry1 55 | run: | 56 | git reset --hard 57 | git clean -fd 58 | npm run test-ui 59 | 60 | - name: Retry UI Test 2 61 | continue-on-error: true 62 | if: steps.retry1.outcome=='failure' 63 | id: retry2 64 | run: | 65 | git reset --hard 66 | git clean -fd 67 | npm run test-ui 68 | 69 | - name: Set test status 70 | if: ${{ steps.test.outcome=='failure' && steps.retry1.outcome=='failure' && steps.retry2.outcome=='failure' }} 71 | run: | 72 | echo "Tests failed" 73 | exit 1 74 | 75 | - name: Print language server Log if job failed 76 | if: ${{ failure() }} 77 | run: Get-ChildItem -Path ./test-resources/settings/User/workspaceStorage/*/redhat.java/jdt_ws/.metadata/.log | cat 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | .DS_Store 3 | target/ 4 | bin/ 5 | out/ 6 | **/lib 7 | **/server 8 | **/*.vsix 9 | .vscode-test/ 10 | vscode-java-*.vsix 11 | packages/ 12 | dist 13 | **/.settings 14 | **/.classpath 15 | **/.project 16 | **/.checkstyle 17 | test-resources/ 18 | **/.gradle 19 | -------------------------------------------------------------------------------- /.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 | "shengchen.vscode-checkstyle", 6 | "yaozheng.vscode-pde" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.configuration.updateBuildConfiguration": "automatic", 3 | "java.checkstyle.configuration": "${workspaceFolder}/jdtls.ext/check_style.xml", 4 | "java.checkstyle.version": "8.29", 5 | "java.import.exclusions": [ 6 | "**/test/**", 7 | "**/node_modules/**", 8 | "**/.metadata/**", 9 | "**/archetype-resources/**", 10 | "**/META-INF/maven/**", 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.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 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern":[ 12 | { 13 | "regexp": "\\[tsl\\] ERROR", 14 | "file": 1, 15 | "location": 2, 16 | "message": 3 17 | } 18 | ], 19 | "background": { 20 | "activeOnStart": true, 21 | "beginsPattern": "\\w+", 22 | "endsPattern": "webpack .* compiled" 23 | } 24 | }, 25 | "isBackground": true, 26 | "presentation": { 27 | "reveal": "never" 28 | }, 29 | "group": { 30 | "kind": "build", 31 | "isDefault": true 32 | } 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/** 4 | test/** 5 | src/** 6 | **/*.map 7 | jdtls.ext/** 8 | vscode/** 9 | tsconfig.json 10 | scripts/build/** 11 | .gitignore 12 | dist/**/test 13 | webpack.*.js 14 | tslint.json 15 | node_modules 16 | scripts 17 | extension.bundle.ts 18 | javaConfig.json 19 | .github/** 20 | .azure-pipelines/** 21 | images 22 | test-resources 23 | 24 | # Ignore output of code sign 25 | server/*.md 26 | **/*.log 27 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We greatly appreciate contributions to the vscode-java-dependency project. Your efforts help us maintain and improve this extension. To ensure a smooth contribution process, please follow these guidelines. 4 | 5 | ## Prerequisites 6 | - [JDK](https://www.oracle.com/java/technologies/downloads/?er=221886) 7 | - [Node.JS](https://nodejs.org/en/) 8 | - [VSCode](https://code.visualstudio.com/) 9 | 10 | ## Build and Run 11 | 12 | To set up the vscode-java-dependency project, follow these steps: 13 | 14 | 1. **Build the Server JAR**: 15 | - The server JAR (Java application) is located in the [jdtls.ext](./jdtls.ext) directory. 16 | - Run the following command to build the server: 17 | ```shell 18 | npm run build-server 19 | ``` 20 | 21 | 2. **Install Dependencies**: 22 | - Execute the following command to install the necessary dependencies: 23 | ```shell 24 | npm install 25 | ``` 26 | 27 | 3. **Run/Debug the Extension**: 28 | - Open the "Run and Debug" view in Visual Studio Code. 29 | - Run the "Run Extension" task. 30 | 31 | 4. **Attach to Plugin[Debug Java]**: 32 | - Prerequisite: Ensure that the extension is activated, meaning the Java process is already launched. This is required for the task to run properly. 33 | - Open the "Run and Debug" view in Visual Studio Code. 34 | - Run the "Attach to Plugin" task. 35 | - Note: This task is required only if you want to debug Java code [jdtls.ext](./jdtls.ext). It requires the [vscode-pde](https://marketplace.visualstudio.com/items?itemName=yaozheng.vscode-pde) extension to be installed. 36 | 37 | Thank you for your contributions and support! -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project Manager for Java 2 | 3 | > Manage Java projects in Visual Studio Code 4 | 5 | [![GitHub Actions](https://img.shields.io/github/actions/workflow/status/microsoft/vscode-java-dependency/windows.yml?label=Windows%20Build&style=flat-square)](https://github.com/microsoft/vscode-java-dependency/actions/workflows/windows.yml?query=branch%3Amain) 6 | [![GitHub Actions](https://img.shields.io/github/actions/workflow/status/microsoft/vscode-java-dependency/linux.yml?label=Linux%20Build&style=flat-square)](https://github.com/microsoft/vscode-java-dependency/actions/workflows/linux.yml?query=branch%3Amain) 7 | [![GitHub Actions](https://img.shields.io/github/actions/workflow/status/microsoft/vscode-java-dependency/macOS.yml?label=Darwin%20Build&style=flat-square)](https://github.com/microsoft/vscode-java-dependency/actions/workflows/macOS.yml?query=branch%3Amain) 8 | 9 | ## Overview 10 | 11 | A lightweight extension to provide additional Java project explorer features. It works with [Language Support for Java by Red Hat](https://marketplace.visualstudio.com/items?itemName=redhat.java) to provide the following features: 12 | 13 | ### Project View 14 | 15 | ![project-view](https://raw.githubusercontent.com/Microsoft/vscode-java-dependency/main/images/project-explorer.png) 16 | 17 | ### Create Java Projects 18 | 19 | ![create project](https://raw.githubusercontent.com/Microsoft/vscode-java-dependency/main/images/create-project.png) 20 | 21 | ### Export Jar 22 | > Note: For Spring Boot projects, please use the build tool to build the executable jar, for example: `mvn package`. 23 | 24 | ![export jar](https://raw.githubusercontent.com/Microsoft/vscode-java-dependency/main/images/export-jar.png) 25 | 26 | ### Manage Dependencies 27 | 28 | You can work with JAR files directly without any build tools. Go to `JAVA PROJECTS` view, find the `Referenced Libraries` node and click the `+` icon: 29 | 30 | ![Reference JAR Files](https://raw.githubusercontent.com/Microsoft/vscode-java-dependency/main/images/manage-dependencies.gif) 31 | 32 | If you want to fine-tune this, go to `settings.json` and look for the `java.project.referencedLibraries` entry. 33 | 34 | ```json 35 | "java.project.referencedLibraries": [ 36 | "library/**/*.jar", 37 | "/home/username/lib/foo.jar" 38 | ] 39 | ``` 40 | 41 | You can tell that the glob pattern is supported. And here's more - you can include/exclude certain files, and attach source JARs: 42 | 43 | ```json 44 | "java.project.referencedLibraries": { 45 | "include": [ 46 | "library/**/*.jar", 47 | "/home/username/lib/foo.jar" 48 | ], 49 | "exclude": [ 50 | "library/sources/**" 51 | ], 52 | "sources": { 53 | "library/bar.jar": "library/sources/bar-src.jar" 54 | } 55 | } 56 | ``` 57 | 58 | ## Requirements 59 | 60 | - VS Code (version 1.83.1+) 61 | - [Language Support for Java by Red Hat](https://marketplace.visualstudio.com/items?itemName=redhat.java) 62 | 63 | 64 | ## Settings 65 | 66 | | Setting Name | Description | Default Value | 67 | |---|---|---| 68 | | `java.dependency.showMembers` | Specify whether to show the members in the Java Projects explorer. | `false` | 69 | | `java.dependency.syncWithFolderExplorer` | Specify whether to link Java Projects Explorer with the active editor. | `true` | 70 | | `java.dependency.autoRefresh` | Specify whether to automatically sync the change from editor to the Java Projects explorer. | `true` | 71 | | `java.dependency.refreshDelay` | The delay time (ms) the auto refresh is invoked when changes are detected. | `2000ms` | 72 | | `java.dependency.packagePresentation` | Specify how to display the package. Supported values are: `flat`, `hierarchical`.| `flat` | 73 | | `java.project.exportJar.targetPath` | The output path of export jar. When this setting is **empty** , a file explorer will pop up to let the user select the output location.| `${workspaceFolder}/${workspaceFolderBasename}.jar` | 74 | | `java.project.explorer.showNonJavaResources` | When enabled, the explorer shows non-Java resources. | `true` | 75 | 76 | ## Contribution 77 | 78 | ### Build 79 | * Prerequirement 80 | - Node.js 81 | - Java SDK 11 or above 82 | 83 | * Go to root folder: 84 | ``` 85 | npm install 86 | npm run build-server 87 | ``` 88 | 89 | ## Telemetry 90 | VS Code collects usage data and sends it to Microsoft to help improve our products and services. Read our [privacy statement](https://go.microsoft.com/fwlink/?LinkID=528096&clcid=0x409) to learn more. If you don't wish to send usage data to Microsoft, you can set the `telemetry.enableTelemetry` setting to `false`. Learn more in our [FAQ](https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting). 91 | 92 | 93 | --- 94 | 95 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 96 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /extension.bundle.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | export { activate, deactivate } from "./src/extension"; 5 | 6 | // context value test 7 | export { INodeData, NodeKind, TypeKind } from "./src/java/nodeData"; 8 | export { PackageRootKind } from "./src/java/packageRootNodeData"; 9 | export { ContainerNode } from "./src/views/containerNode"; 10 | export { DataNode } from "./src/views/dataNode"; 11 | export { FileNode } from "./src/views/fileNode"; 12 | export { FolderNode } from "./src/views/folderNode"; 13 | export { PackageNode } from "./src/views/packageNode"; 14 | export { PackageRootNode } from "./src/views/packageRootNode"; 15 | export { PrimaryTypeNode } from "./src/views/PrimaryTypeNode"; 16 | export { ProjectNode } from "./src/views/projectNode"; 17 | export { WorkspaceNode } from "./src/views/workspaceNode"; 18 | export { IMainClassInfo } from "./src/tasks/buildArtifact/ResolveMainClassExecutor"; 19 | 20 | // project view test 21 | export { contextManager } from "./src/contextManager"; 22 | export { DependencyExplorer } from "./src/views/dependencyExplorer"; 23 | export { Commands } from "./src/commands"; 24 | export { LanguageServerMode } from "./src/languageServerApi/LanguageServerMode"; 25 | 26 | // language server api 27 | export { languageServerApiManager } from "./src/languageServerApi/languageServerApiManager"; 28 | 29 | // tasks 30 | export { BuildTaskProvider, categorizePaths, getFinalPaths } from "./src/tasks/build/buildTaskProvider"; 31 | 32 | // delegate commands 33 | export { Jdtls } from "./src/java/jdtls"; 34 | -------------------------------------------------------------------------------- /icons/dark/icon-link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /icons/dark/icon-unlink.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /icons/light/icon-link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /icons/light/icon-unlink.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /images/create-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-java-dependency/7b3990cf7d388b0e3fc801734cbff654a5313d17/images/create-project.png -------------------------------------------------------------------------------- /images/export-jar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-java-dependency/7b3990cf7d388b0e3fc801734cbff654a5313d17/images/export-jar.png -------------------------------------------------------------------------------- /images/manage-dependencies.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-java-dependency/7b3990cf7d388b0e3fc801734cbff654a5313d17/images/manage-dependencies.gif -------------------------------------------------------------------------------- /images/project-explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-java-dependency/7b3990cf7d388b0e3fc801734cbff654a5313d17/images/project-explorer.png -------------------------------------------------------------------------------- /javaConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ 3 | "./jdtls.ext/com.microsoft.jdtls.ext.core" 4 | ], 5 | "targetPlatform": "./jdtls.ext/com.microsoft.jdtls.ext.target/com.microsoft.jdtls.ext.tp.target" 6 | } 7 | -------------------------------------------------------------------------------- /jdtls.ext/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-java-dependency/7b3990cf7d388b0e3fc801734cbff654a5313d17/jdtls.ext/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /jdtls.ext/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.microsoft.jdtls.ext.core 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.pde.ManifestBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.pde.SchemaBuilder 20 | 21 | 22 | 23 | 24 | org.eclipse.m2e.core.maven2Builder 25 | 26 | 27 | 28 | 29 | 30 | org.eclipse.m2e.core.maven2Nature 31 | org.eclipse.pde.PluginNature 32 | org.eclipse.jdt.core.javanature 33 | 34 | 35 | 36 | 1665551209562 37 | 38 | 30 39 | 40 | org.eclipse.core.resources.regexFilterMatcher 41 | node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/.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=11 4 | org.eclipse.jdt.core.compiler.compliance=11 5 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 6 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 7 | org.eclipse.jdt.core.compiler.release=disabled 8 | org.eclipse.jdt.core.compiler.source=11 9 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Bundle-ManifestVersion: 2 3 | Bundle-Name: JDTLS EXT Core 4 | Bundle-SymbolicName: com.microsoft.jdtls.ext.core;singleton:=true 5 | Bundle-Version: 0.24.1 6 | Bundle-Activator: com.microsoft.jdtls.ext.core.JdtlsExtActivator 7 | Bundle-RequiredExecutionEnvironment: JavaSE-11 8 | Bundle-ActivationPolicy: lazy 9 | Import-Package: org.eclipse.jdt.core, 10 | org.eclipse.jdt.launching, 11 | org.osgi.framework;version="1.3.0" 12 | Require-Bundle: org.eclipse.core.runtime, 13 | org.eclipse.jdt.ls.core, 14 | org.eclipse.jdt.core, 15 | org.eclipse.core.resources, 16 | org.eclipse.lsp4j.jsonrpc, 17 | org.eclipse.lsp4j, 18 | org.apache.commons.lang3, 19 | com.google.gson 20 | Bundle-ClassPath: . 21 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/build.properties: -------------------------------------------------------------------------------- 1 | source.. = src/ 2 | output.. = bin/ 3 | bin.includes = META-INF/,\ 4 | .,\ 5 | plugin.xml 6 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.microsoft.jdtls.ext 7 | jdtls-ext-parent 8 | 0.24.1 9 | 10 | com.microsoft.jdtls.ext.core 11 | eclipse-plugin 12 | ${base.name} :: JDTLS Ext Plugin 13 | 14 | 15 | 16 | 17 | 18 | org.apache.maven.plugins 19 | maven-checkstyle-plugin 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/CommandHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018 Microsoft Corporation and others. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Microsoft Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | package com.microsoft.jdtls.ext.core; 13 | 14 | import java.util.List; 15 | 16 | import org.apache.commons.lang3.StringUtils; 17 | import org.eclipse.core.runtime.IProgressMonitor; 18 | import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler; 19 | 20 | public class CommandHandler implements IDelegateCommandHandler { 21 | 22 | @Override 23 | public Object executeCommand(String commandId, List arguments, IProgressMonitor monitor) throws Exception { 24 | if (!StringUtils.isBlank(commandId)) { 25 | switch (commandId) { 26 | case "java.project.list": 27 | return ProjectCommand.listProjects(arguments, monitor); 28 | case "java.project.refreshLib": 29 | return ProjectCommand.refreshLibraries(arguments, monitor); 30 | case "java.getPackageData": 31 | return PackageCommand.getChildren(arguments, monitor); 32 | case "java.resolvePath": 33 | return PackageCommand.resolvePath(arguments, monitor); 34 | case "java.project.getMainClasses": 35 | return ProjectCommand.getMainClasses(arguments, monitor); 36 | case "java.project.generateJar": 37 | return ProjectCommand.exportJar(arguments, monitor); 38 | case "java.project.checkImportStatus": 39 | return ProjectCommand.checkImportStatus(); 40 | default: 41 | break; 42 | } 43 | } 44 | throw new UnsupportedOperationException(String.format("Not supported commandId: '%s'.", commandId)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/JarFileContentProvider.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018 Microsoft Corporation and others. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Microsoft Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | package com.microsoft.jdtls.ext.core; 13 | 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.net.URI; 17 | import java.nio.charset.StandardCharsets; 18 | 19 | import org.eclipse.core.runtime.CoreException; 20 | import org.eclipse.core.runtime.IProgressMonitor; 21 | import org.eclipse.core.runtime.IStatus; 22 | import org.eclipse.core.runtime.Status; 23 | import org.eclipse.jdt.core.IPackageFragmentRoot; 24 | import org.eclipse.jdt.core.JavaCore; 25 | import org.eclipse.jdt.internal.core.JarEntryFile; 26 | import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; 27 | import org.eclipse.jdt.ls.core.internal.IContentProvider; 28 | import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; 29 | 30 | /** 31 | * Get file content from the JarEntryFile contained inside a .jar file. 32 | */ 33 | public class JarFileContentProvider implements IContentProvider { 34 | 35 | @Override 36 | public String getContent(URI uri, IProgressMonitor monitor) throws CoreException { 37 | return getContent(uri.getQuery(), uri.getPath().toString(), monitor); 38 | } 39 | 40 | private String getContent(String rootId, String path, IProgressMonitor pm) { 41 | try { 42 | IPackageFragmentRoot packageRoot = (IPackageFragmentRoot) JavaCore.create(rootId); 43 | if (packageRoot == null) { 44 | throw new CoreException(new Status(IStatus.ERROR, JdtlsExtActivator.PLUGIN_ID, String.format("No package root found for %s", rootId))); 45 | } 46 | 47 | if (packageRoot instanceof JarPackageFragmentRoot) { 48 | JarEntryFile fileEntry = ExtUtils.findJarEntryFile(packageRoot, path); 49 | if (fileEntry != null) { 50 | return readFileContent(fileEntry); 51 | } 52 | 53 | } 54 | } catch (CoreException e) { 55 | JavaLanguageServerPlugin.logException("Problem get JarEntryFile content ", e); 56 | } 57 | return null; 58 | } 59 | 60 | private static String readFileContent(JarEntryFile file) throws CoreException { 61 | try (InputStream stream = (file.getContents())) { 62 | return convertStreamToString(stream); 63 | } catch (IOException e) { 64 | throw new CoreException(new Status(IStatus.ERROR, JdtlsExtActivator.PLUGIN_ID, "Can't read file content: " + file.getFullPath())); 65 | } 66 | } 67 | 68 | private static String convertStreamToString(InputStream inputStream) throws IOException { 69 | return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/JdtlsExtActivator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018 Microsoft Corporation and others. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Microsoft Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | package com.microsoft.jdtls.ext.core; 13 | 14 | import org.eclipse.core.runtime.CoreException; 15 | import org.eclipse.core.runtime.IStatus; 16 | import org.eclipse.core.runtime.Platform; 17 | import org.eclipse.core.runtime.Status; 18 | import org.osgi.framework.BundleActivator; 19 | import org.osgi.framework.BundleContext; 20 | 21 | public class JdtlsExtActivator implements BundleActivator { 22 | 23 | public static final String PLUGIN_ID = "org.eclipse.jdtls.ext.core"; 24 | 25 | private static BundleContext context; 26 | 27 | static BundleContext getContext() { 28 | return context; 29 | } 30 | 31 | @Override 32 | public void start(BundleContext bundleContext) throws Exception { 33 | JdtlsExtActivator.context = bundleContext; 34 | } 35 | 36 | @Override 37 | public void stop(BundleContext bundleContext) throws Exception { 38 | JdtlsExtActivator.context = null; 39 | } 40 | 41 | public static void log(IStatus status) { 42 | if (context != null) { 43 | Platform.getLog(context.getBundle()).log(status); 44 | } 45 | } 46 | 47 | public static void log(CoreException e) { 48 | log(e.getStatus()); 49 | } 50 | 51 | public static void logError(String message) { 52 | if (context != null) { 53 | log(new Status(IStatus.ERROR, context.getBundle().getSymbolicName(), message)); 54 | } 55 | } 56 | 57 | public static void logInfo(String message) { 58 | if (context != null) { 59 | log(new Status(IStatus.INFO, context.getBundle().getSymbolicName(), message)); 60 | } 61 | } 62 | 63 | public static void logException(String message, Throwable ex) { 64 | if (context != null) { 65 | log(new Status(IStatus.ERROR, context.getBundle().getSymbolicName(), message, ex)); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageParams.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018 Microsoft Corporation and others. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Microsoft Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | package com.microsoft.jdtls.ext.core; 13 | 14 | import com.microsoft.jdtls.ext.core.model.NodeKind; 15 | 16 | /** 17 | * The query object to get the project dependency information from the language 18 | * server. 19 | */ 20 | public class PackageParams { 21 | 22 | private NodeKind kind; 23 | 24 | private String projectUri; 25 | 26 | private String path; 27 | 28 | private String handlerIdentifier; 29 | 30 | private String rootPath; 31 | 32 | private boolean isHierarchicalView; 33 | 34 | public PackageParams() { 35 | } 36 | 37 | public boolean isHierarchicalView() { 38 | return isHierarchicalView; 39 | } 40 | 41 | public void setHierarchicalView(boolean isHierarchicalView) { 42 | this.isHierarchicalView = isHierarchicalView; 43 | } 44 | 45 | public PackageParams(NodeKind kind, String projectUri) { 46 | this.kind = kind; 47 | this.projectUri = projectUri; 48 | } 49 | 50 | public PackageParams(NodeKind kind, String projectUri, String path) { 51 | this(kind, projectUri); 52 | this.path = path; 53 | } 54 | 55 | public PackageParams(NodeKind kind, String projectUri, String path, String handlerIdentifier) { 56 | this(kind, projectUri, path); 57 | this.handlerIdentifier = handlerIdentifier; 58 | } 59 | 60 | public NodeKind getKind() { 61 | return kind; 62 | } 63 | 64 | public void setKind(NodeKind kind) { 65 | this.kind = kind; 66 | } 67 | 68 | public String getProjectUri() { 69 | return projectUri; 70 | } 71 | 72 | public void setProjectUri(String projectUri) { 73 | this.projectUri = projectUri; 74 | } 75 | 76 | public String getPath() { 77 | return path; 78 | } 79 | 80 | public void setPath(String nodePath) { 81 | this.path = nodePath; 82 | } 83 | 84 | public String getHandlerIdentifier() { 85 | return handlerIdentifier; 86 | } 87 | 88 | public void setHandlerIdentifier(String handlerIdentifier) { 89 | this.handlerIdentifier = handlerIdentifier; 90 | } 91 | 92 | public String getRootPath() { 93 | return rootPath; 94 | } 95 | 96 | public void setRootPath(String rootPath) { 97 | this.rootPath = rootPath; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/ContainerNode.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018 Microsoft Corporation and others. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Microsoft Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | package com.microsoft.jdtls.ext.core.model; 13 | 14 | public class ContainerNode extends PackageNode { 15 | 16 | private int entryKind; 17 | 18 | public ContainerNode(String name, String path, NodeKind kind, int entryKind) { 19 | super(name, path, kind); 20 | this.entryKind = entryKind; 21 | } 22 | 23 | public int getEntryType() { 24 | return this.entryKind; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/NodeKind.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018 Microsoft Corporation and others. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Microsoft Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | package com.microsoft.jdtls.ext.core.model; 13 | 14 | public enum NodeKind { 15 | WORKSPACE(1), 16 | 17 | PROJECT(2), 18 | 19 | PACKAGEROOT(3), 20 | 21 | PACKAGE(4), 22 | 23 | PRIMARYTYPE(5), 24 | 25 | COMPILATIONUNIT(6), 26 | 27 | CLASSFILE(7), 28 | 29 | CONTAINER(8), 30 | 31 | FOLDER(9), 32 | 33 | FILE(10); 34 | 35 | private final int value; 36 | 37 | NodeKind(int value) { 38 | this.value = value; 39 | } 40 | 41 | public int getValue() { 42 | return value; 43 | } 44 | 45 | public static NodeKind forValue(int value) { 46 | NodeKind[] allValues = NodeKind.values(); 47 | if (value < 1 || value > allValues.length) { 48 | throw new IllegalArgumentException("Illegal enum value: " + value); 49 | } 50 | return allValues[value - 1]; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/PackageRootNode.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018 Microsoft Corporation and others. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Microsoft Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | package com.microsoft.jdtls.ext.core.model; 13 | 14 | import java.util.Map; 15 | 16 | import org.eclipse.jdt.core.IPackageFragmentRoot; 17 | import org.eclipse.jdt.core.JavaModelException; 18 | 19 | public class PackageRootNode extends PackageNode { 20 | 21 | private int entryKind; 22 | 23 | private Map attributes; 24 | 25 | public PackageRootNode(String name, String path, String uri, NodeKind kind, int entryKind) { 26 | super(name, path, kind); 27 | this.setUri(uri); 28 | this.entryKind = entryKind; 29 | } 30 | 31 | public PackageRootNode(IPackageFragmentRoot pkgRoot, String name, NodeKind kind) throws JavaModelException { 32 | this(name, pkgRoot.getPath().toPortableString(), null, kind, pkgRoot.getKind()); 33 | if (pkgRoot.getResource() != null) { 34 | this.setUri(pkgRoot.getResource().getLocationURI().toString()); 35 | } else { 36 | this.setUri(pkgRoot.getPath().toFile().toURI().toString()); 37 | } 38 | } 39 | 40 | public int getEntryType() { 41 | return this.entryKind; 42 | } 43 | 44 | public void setAttributes(Map attributes) { 45 | this.attributes = attributes; 46 | } 47 | 48 | public Map getAttributes() { 49 | return this.attributes; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/Trie.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021 Microsoft Corporation and others. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Microsoft Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | package com.microsoft.jdtls.ext.core.model; 13 | 14 | import java.util.HashSet; 15 | import java.util.Map; 16 | import java.util.Set; 17 | 18 | import org.apache.commons.lang3.StringUtils; 19 | 20 | public class Trie { 21 | private TrieNode root = new TrieNode<>(); 22 | private Set> allNodes = new HashSet<>(); 23 | 24 | public Trie(Map entries) { 25 | for (Map.Entry entry : entries.entrySet()) { 26 | insert(entry.getKey(), entry.getValue()); 27 | } 28 | } 29 | 30 | public Set> getAllNodes() { 31 | return allNodes; 32 | } 33 | 34 | public void insert(String name, T value) { 35 | if (StringUtils.isBlank(name)) { 36 | // default package 37 | root.value = value; 38 | allNodes.add(root); 39 | return; 40 | } 41 | 42 | String[] names = name.split("\\."); 43 | TrieNode currentNode = this.root; 44 | for (int i = 0; i < names.length; i++) { 45 | TrieNode node; 46 | if (currentNode.children.containsKey(names[i])) { 47 | node = currentNode.children.get(names[i]); 48 | } else { 49 | node = new TrieNode(names[i], null); 50 | currentNode.children.put(names[i], node); 51 | allNodes.add(node); 52 | } 53 | if (i == names.length - 1) { 54 | node.value = value; 55 | } 56 | 57 | currentNode = node; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/TrieNode.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021 Microsoft Corporation and others. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Microsoft Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | package com.microsoft.jdtls.ext.core.model; 13 | 14 | import java.util.LinkedHashMap; 15 | import java.util.Map; 16 | 17 | public class TrieNode { 18 | public String name; 19 | public Map> children = new LinkedHashMap<>(); 20 | public T value; 21 | 22 | public TrieNode() { 23 | } 24 | 25 | public TrieNode(String name, T value) { 26 | this.name = name; 27 | this.value = value; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/parser/ResourceVisitor.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2023 Microsoft Corporation and others. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Microsoft Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | package com.microsoft.jdtls.ext.core.parser; 13 | 14 | import java.util.List; 15 | 16 | import org.eclipse.core.resources.IFile; 17 | import org.eclipse.core.resources.IFolder; 18 | import org.eclipse.jdt.core.IClassFile; 19 | import org.eclipse.jdt.core.IClasspathEntry; 20 | import org.eclipse.jdt.core.ICompilationUnit; 21 | import org.eclipse.jdt.core.IJarEntryResource; 22 | import org.eclipse.jdt.core.IPackageFragment; 23 | import org.eclipse.jdt.core.IPackageFragmentRoot; 24 | import org.eclipse.jdt.core.IType; 25 | 26 | import com.microsoft.jdtls.ext.core.model.PackageNode; 27 | 28 | /** 29 | * A visitor to iterate through resources in a project and parse them to 30 | * nodes in the UI. 31 | */ 32 | public interface ResourceVisitor { 33 | 34 | void visit(IClasspathEntry entry); 35 | 36 | void visit(IPackageFragmentRoot packageFragmentRoot); 37 | 38 | void visit(IPackageFragment fragment); 39 | 40 | void visit(IType type); 41 | 42 | void visit(IClassFile classFile); 43 | 44 | void visit(ICompilationUnit compilationUnit); 45 | 46 | void visit(IFile file); 47 | 48 | void visit(IFolder folder); 49 | 50 | void visit(IJarEntryResource jarEntryResource); 51 | 52 | List getNodes(); 53 | } 54 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.target/com.microsoft.jdtls.ext.tp.target: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | org.apache.commons 27 | commons-lang3 28 | 3.14.0 29 | jar 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /jdtls.ext/com.microsoft.jdtls.ext.target/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.microsoft.jdtls.ext 6 | jdtls-ext-parent 7 | 0.24.1 8 | 9 | com.microsoft.jdtls.ext.tp 10 | ${base.name} :: Target Platform 11 | eclipse-target-definition 12 | 13 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-java-dependency/7b3990cf7d388b0e3fc801734cbff654a5313d17/logo.png -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | "use strict"; 5 | 6 | Object.defineProperty(exports, "__esModule", { value: true }); 7 | 8 | const extension = require("./dist/extension.bundle"); 9 | 10 | async function activate(ctx) { 11 | return await extension.activate(ctx); 12 | } 13 | 14 | async function deactivate(ctx) { 15 | return await extension.deactivate(ctx); 16 | } 17 | 18 | // Export as entrypoints for vscode 19 | exports.activate = activate; 20 | exports.deactivate = deactivate; 21 | -------------------------------------------------------------------------------- /package.nls.zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "在 Visual Studio Code 中管理 Java 專案", 3 | "contributes.commands.java.project.create": "建立 Java 專案...", 4 | "contributes.commands.java.project.new": "建立 Java 專案...", 5 | "contributes.commands.java.project.addLibraries": "新增 Jar 檔案至專案 Classpath...", 6 | "contributes.commands.java.project.addLibraryFolders": "新增資料夾至專案 Classpath...", 7 | "contributes.commands.java.project.removeLibrary": "從專案 Classpath 中移除", 8 | "contributes.commands.java.view.package.refresh": "重新整理", 9 | "contributes.commands.java.project.build.workspace": "重新建置所有專案", 10 | "contributes.commands.java.project.clean.workspace": "清理工作區", 11 | "contributes.commands.java.project.rebuild": "重新建置專案", 12 | "contributes.commands.java.project.update": "重新載入專案", 13 | "contributes.commands.java.project.reloadProjectFromActiveFile": "重新載入 Java 專案", 14 | "contributes.commands.java.view.package.revealInProjectExplorer": "在 Java 專案視圖中顯示", 15 | "contributes.commands.java.view.package.changeToFlatPackageView":"平行顯示", 16 | "contributes.commands.java.view.package.changeToHierarchicalPackageView":"階層顯示", 17 | "contributes.commands.java.view.package.linkWithFolderExplorer":"與編輯器同步", 18 | "contributes.commands.java.view.package.unlinkWithFolderExplorer":"與編輯器解除同步", 19 | "contributes.commands.java.view.package.revealFileInOS": "在檔案總管中顯示", 20 | "contributes.commands.java.view.package.exportJar": "匯出成 Jar 檔案...", 21 | "contributes.commands.java.view.package.copyFilePath": "複製路徑", 22 | "contributes.commands.java.view.package.copyRelativeFilePath": "複製相對路徑", 23 | "contributes.commands.java.view.package.new": "建立...", 24 | "contributes.commands.java.view.package.newJavaClass": "類別...", 25 | "contributes.commands.java.view.package.newPackage": "套件...", 26 | "contributes.commands.java.view.package.newFile": "檔案...", 27 | "contributes.commands.java.view.package.newFolder": "資料夾...", 28 | "contributes.commands.java.view.package.renameFile": "重新命名", 29 | "contributes.commands.java.view.package.moveFileToTrash": "刪除", 30 | "contributes.commands.java.view.package.deleteFilePermanently": "永久刪除", 31 | "contributes.submenus.javaProject.new": "建立", 32 | "contributes.commands.java.view.menus.file.newJavaClass": "建立 Java 類別", 33 | "configuration.java.dependency.showMembers": "在 Java 專案管理員中顯示成員", 34 | "configuration.java.dependency.syncWithFolderExplorer": "在 Java 專案管理員中同步關聯當前開啟的檔案", 35 | "configuration.java.dependency.autoRefresh": "在 Java 專案管理員中自動同步修改", 36 | "configuration.java.dependency.refreshDelay": "控制 Java 專案管理員重新整理的延遲時間 (毫秒)", 37 | "configuration.java.dependency.packagePresentation": "Java 套件顯示方式: 平行顯示或者分層顯示", 38 | "configuration.java.project.exportJar.targetPath.customization": "匯出 Jar 檔案的路徑。如果您想要手動選擇輸出路徑,可以不填。", 39 | "configuration.java.project.exportJar.targetPath.workspaceFolder": "匯出 Jar 檔案到工作區資料夾下。Jar 檔案的名稱和工作區資料夾的名稱相同。", 40 | "configuration.java.project.exportJar.targetPath.select": "在匯出 Jar 檔案時手動選擇輸出位置。", 41 | "taskDefinitions.java.project.exportJar.label": "匯出 Jar 任務的名稱。", 42 | "taskDefinitions.java.project.exportJar.elements": "匯出 Jar 檔案的內容列表。", 43 | "taskDefinitions.java.project.exportJar.mainClass": "匯出 Jar 檔案的 main 函數所在的類別。", 44 | "taskDefinitions.java.project.exportJar.compileOutput": "在 runtime scope 內包含輸出的類別檔案的資料夾。", 45 | "taskDefinitions.java.project.exportJar.testCompileOutput": "在 test scope 內包含輸出的類別檔案的資料夾。", 46 | "taskDefinitions.java.project.exportJar.dependencies": "在 runtime scope 內的製品相依性。", 47 | "taskDefinitions.java.project.exportJar.testDependencies": "在 test scope 內的製品相依性。", 48 | "taskDefinitions.java.project.build.path": "被建置專案的根目錄路徑。絕對路徑或者相對於工作區目錄的相對路徑都可以使用。", 49 | "taskDefinitions.java.project.build.path.workspace": "工作區中的所有專案。", 50 | "taskDefinitions.java.project.build.path.exclude": "'!' 後的路徑將會從待建置專案路徑中移除。", 51 | "taskDefinitions.java.project.build.isFullBuild": "是否要重新建置專案。", 52 | "viewsWelcome.workbench.createNewJavaProject": "您也可以[開啟一個 Java 專案目錄](command:_java.project.open),或點擊下方按鈕建立一個新的 Java 專案。\n[建立 Java 專案](command:_java.project.create.from.fileexplorer.welcome)", 53 | "viewsWelcome.workbench.noJavaProject": "當前工作區未發現 Java 專案,您可以[開啟一個 Java 專案目錄](command:_java.project.open),或點擊下方按鈕建立一個新的 Java 專案。\n[建立 Java 專案](command:_java.project.create.from.javaprojectexplorer.welcome)", 54 | "viewsWelcome.workbench.importFailed": "加載 Java 專案時出現錯誤,請通過以下方式查看錯誤相關信息:\n[打開問題視圖](command:workbench.panel.markers.view.focus)", 55 | "viewsWelcome.workbench.inLightWeightMode": "若要檢視各專案,你可以將專案匯入到工作區中。\n[匯入專案](command:java.server.mode.switch?%5B%22Standard%22,true%5D)", 56 | "viewsWelcome.workbench.installLanguageSupport": "Java 專案視圖需要安裝並啟用 [Extension Pack for Java](command:extension.open?%5B%22vscjava.vscode-java-pack%22%5D) 以提供完整的功能。\n[安裝](command:java.project.installExtension?%5B%22vscjava.vscode-java-pack%22%5D)" 57 | } -------------------------------------------------------------------------------- /scripts/buildJdtlsExt.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | const cp = require('child_process'); 5 | const fse = require('fs-extra'); 6 | const path = require('path'); 7 | 8 | const server_dir = path.resolve('jdtls.ext'); 9 | 10 | cp.execSync(mvnw()+ ' clean package', {cwd:server_dir, stdio:[0,1,2]} ); 11 | copy(path.join(server_dir, 'com.microsoft.jdtls.ext.core/target'), path.resolve('server'), (file) => { 12 | return /^com.microsoft.jdtls.ext.core.*.jar$/.test(file); 13 | }); 14 | 15 | function copy(sourceFolder, targetFolder, fileFilter) { 16 | const jars = fse.readdirSync(sourceFolder).filter(file => fileFilter(file)); 17 | fse.ensureDirSync(targetFolder); 18 | for (const jar of jars) { 19 | fse.copyFileSync(path.join(sourceFolder, jar), path.join(targetFolder, path.basename(jar))); 20 | } 21 | } 22 | 23 | function isWin() { 24 | return /^win/.test(process.platform); 25 | } 26 | 27 | function mvnw() { 28 | return isWin()?"mvnw.cmd":"./mvnw"; 29 | } 30 | -------------------------------------------------------------------------------- /scripts/prepare-nightly-build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | const json = JSON.parse(fs.readFileSync("./package.json").toString()); 4 | const stableVersion = json.version.match(/(\d+)\.(\d+)\.(\d+)/); 5 | const major = stableVersion[1]; 6 | const minor = stableVersion[2]; 7 | 8 | function prependZero(number) { 9 | if (number > 99) { 10 | throw "Unexpected value to prepend with zero"; 11 | } 12 | return `${number < 10 ? "0" : ""}${number}`; 13 | } 14 | 15 | const date = new Date(); 16 | const month = date.getMonth() + 1; 17 | const day = date.getDate(); 18 | const hours = date.getHours(); 19 | patch = `${date.getFullYear()}${prependZero(month)}${prependZero(day)}${prependZero(hours)}`; 20 | 21 | const insiderPackageJson = Object.assign(json, { 22 | version: `${major}.${minor}.${patch}`, 23 | }); 24 | 25 | fs.writeFileSync("./package.insiders.json", JSON.stringify(insiderPackageJson)); -------------------------------------------------------------------------------- /src/ExperimentationService.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as vscode from "vscode"; 5 | import { addContextProperty, sendInfo } from "vscode-extension-telemetry-wrapper"; 6 | import { getExperimentationServiceAsync, IExperimentationService, IExperimentationTelemetry, TargetPopulation } from "vscode-tas-client"; 7 | 8 | class ExperimentationTelemetry implements IExperimentationTelemetry { 9 | 10 | public setSharedProperty(name: string, value: string): void { 11 | addContextProperty(name, value); 12 | } 13 | 14 | public postEvent(eventName: string, props: Map): void { 15 | const payload: any = { __event_name__: eventName }; 16 | for (const [key, value] of props) { 17 | payload[key] = value; 18 | } 19 | 20 | sendInfo("", payload); 21 | } 22 | } 23 | 24 | let expService: IExperimentationService; 25 | 26 | export function getExpService() { 27 | return expService; 28 | } 29 | 30 | export async function init(context: vscode.ExtensionContext): Promise { 31 | const packageJson: {[key: string]: any} = require("../package.json"); 32 | // tslint:disable: no-string-literal 33 | const extensionName = `${packageJson["publisher"]}.${packageJson["name"]}`; 34 | const extensionVersion = packageJson["version"]; 35 | // tslint:enable: no-string-literal 36 | expService = await getExperimentationServiceAsync(extensionName, extensionVersion, 37 | TargetPopulation.Public, new ExperimentationTelemetry(), context.globalState); 38 | } 39 | -------------------------------------------------------------------------------- /src/build.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { basename } from "path"; 5 | import { commands, DiagnosticSeverity, languages, QuickPickItem, Uri, window } from "vscode"; 6 | import { instrumentOperation, sendInfo, sendOperationError, setErrorCode } from "vscode-extension-telemetry-wrapper"; 7 | import { Commands, executeJavaExtensionCommand } from "./commands"; 8 | import { Jdtls } from "./java/jdtls"; 9 | import { UserError } from "./utility"; 10 | 11 | export async function buildWorkspace(): Promise { 12 | const buildResult = await instrumentOperation("build", async (operationId: string) => { 13 | let error; 14 | try { 15 | await executeJavaExtensionCommand(Commands.JAVA_BUILD_WORKSPACE, false); 16 | } catch (err) { 17 | error = err; 18 | } 19 | 20 | return { 21 | error, 22 | operationId, 23 | }; 24 | })(); 25 | 26 | if (buildResult.error) { 27 | return handleBuildFailure(buildResult.operationId, buildResult.error); 28 | } 29 | return true; 30 | } 31 | 32 | async function handleBuildFailure(operationId: string, err: any): Promise { 33 | 34 | const error: Error = new UserError({ 35 | message: "Build failed", 36 | }); 37 | setErrorCode(error, Number(err)); 38 | sendOperationError(operationId, "build", error); 39 | // Workaround: Since VS Code 1.53, the contributed command would no longer throw exact error message when an error occurs. 40 | // This change breaks the existing build error reporting, so we make a workaround here. 41 | // Related issue: https://github.com/microsoft/vscode/issues/116932 42 | if (err instanceof Error || err === Jdtls.CompileWorkspaceStatus.Witherror || err === Jdtls.CompileWorkspaceStatus.Failed) { 43 | if (checkErrorsReportedByJavaExtension()) { 44 | commands.executeCommand("workbench.actions.view.problems"); 45 | } 46 | 47 | const ans = await window.showErrorMessage("Build failed, do you want to continue?", 48 | "Proceed", "Fix...", "Cancel"); 49 | sendInfo(operationId, { 50 | operationName: "build", 51 | choiceForBuildError: ans || "esc", 52 | }); 53 | if (ans === "Proceed") { 54 | return true; 55 | } else if (ans === "Fix...") { 56 | showFixSuggestions(operationId); 57 | } 58 | return false; 59 | } 60 | return false; 61 | } 62 | 63 | export function checkErrorsReportedByJavaExtension(): boolean { 64 | const problems = languages.getDiagnostics() || []; 65 | for (const problem of problems) { 66 | const fileName = basename(problem[0].fsPath || ""); 67 | if (fileName.endsWith(".java") || fileName === "pom.xml" || fileName.endsWith(".gradle")) { 68 | if (problem[1].filter((diagnostic) => diagnostic.severity === DiagnosticSeverity.Error).length) { 69 | return true; 70 | } 71 | } 72 | } 73 | return false; 74 | } 75 | 76 | async function showFixSuggestions(operationId: string) { 77 | let buildFiles: string[] = []; 78 | try { 79 | buildFiles = await Jdtls.resolveBuildFiles(); 80 | } catch (error) { 81 | // do nothing 82 | } 83 | 84 | const pickitems: QuickPickItem[] = []; 85 | pickitems.push({ 86 | label: "Clean workspace cache", 87 | detail: "Clean the stale workspace and reload the window", 88 | }); 89 | if (buildFiles.length) { 90 | pickitems.push({ 91 | label: "Update project configuration", 92 | detail: "Force the language server to update the project configuration/classpath", 93 | }); 94 | } 95 | pickitems.push({ 96 | label: "Open log file", 97 | detail: "Open log file to view more details for the build errors", 98 | }); 99 | 100 | const ans = await window.showQuickPick(pickitems, { 101 | placeHolder: "Please fix the errors in PROBLEMS first, then try the fix suggestions below.", 102 | }); 103 | sendInfo(operationId, { 104 | operationName: "build", 105 | choiceForBuildFix: ans ? ans.label : "esc", 106 | }); 107 | if (!ans) { 108 | return; 109 | } 110 | 111 | if (ans.label === "Clean workspace cache") { 112 | commands.executeCommand("java.clean.workspace"); 113 | } else if (ans.label === "Update project configuration") { 114 | for (const buildFile of buildFiles) { 115 | await commands.executeCommand("java.projectConfiguration.update", Uri.parse(buildFile)); 116 | } 117 | } else if (ans.label === "Open log file") { 118 | commands.executeCommand("java.open.serverLog"); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | export namespace Context { 5 | export const EXTENSION_ACTIVATED: string = "java:projectManagerActivated"; 6 | export const LANGUAGE_SUPPORT_INSTALLED: string = "java:languageSupportInstalled"; 7 | export const NO_JAVA_PROJECT: string = "java:noJavaProjects"; 8 | export const IMPORT_FAILED: string = "java:importFailed"; 9 | export const WORKSPACE_CONTAINS_BUILD_FILES: string = "java:workspaceContainsBuildFiles"; 10 | export const RELOAD_PROJECT_ACTIVE: string = "java:reloadProjectActive"; 11 | export const SHOW_DEPRECATED_TASKS: string = "java:showDeprecatedTasks"; 12 | } 13 | 14 | export namespace Explorer { 15 | export enum ContextValueType { 16 | WorkspaceFolder = "workspaceFolder", 17 | Project = "project", 18 | Container = "container", 19 | PackageRoot = "packageRoot", 20 | Package = "package", 21 | Jar = "jar", 22 | File = "file", 23 | Type = "type", 24 | Folder = "folder", 25 | Symbol = "symbol", 26 | } 27 | 28 | export enum Mime { 29 | JavaProjectExplorer = "application/vnd.code.tree.javaProjectExplorer", 30 | TextUriList = "text/uri-list", 31 | } 32 | } 33 | 34 | export namespace ExtensionName { 35 | export const JAVA_LANGUAGE_SUPPORT: string = "redhat.java"; 36 | } 37 | 38 | /** 39 | * The files names for all the build files we support. 40 | */ 41 | export const buildFiles = ["pom.xml", "build.gradle", "settings.gradle", "build.gradle.kts", "settings.gradle.kts"]; 42 | -------------------------------------------------------------------------------- /src/contextManager.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { commands, Disposable, ExtensionContext } from "vscode"; 5 | 6 | class ContextManager implements Disposable { 7 | private _context: ExtensionContext; 8 | private _contextValueMap: Map; 9 | 10 | public initialize(context: ExtensionContext) { 11 | this._context = context; 12 | this._contextValueMap = new Map(); 13 | } 14 | 15 | public get context(): ExtensionContext { 16 | return this._context; 17 | } 18 | 19 | public async setContextValue(key: string, value: any): Promise { 20 | this._contextValueMap.set(key, value); 21 | await commands.executeCommand("setContext", key, value); 22 | } 23 | 24 | public getContextValue(key: string): T | undefined { 25 | return this._contextValueMap.get(key); 26 | } 27 | 28 | public dispose(): void { 29 | this._contextValueMap.clear(); 30 | } 31 | } 32 | 33 | export const contextManager: ContextManager = new ContextManager(); 34 | -------------------------------------------------------------------------------- /src/explorerCommands/delete.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { Uri, window, workspace } from "vscode"; 5 | import { sendError } from "vscode-extension-telemetry-wrapper"; 6 | import { DataNode } from "../views/dataNode"; 7 | import { isMutable } from "./utility"; 8 | 9 | export async function deleteFiles(node: DataNode | undefined, useTrash: boolean): Promise { 10 | if (!node?.uri || !isMutable(node)) { 11 | return; 12 | } 13 | 14 | const children = await node.getChildren(); 15 | const isFolder = children && children.length !== 0; 16 | const message = getInformationMessage(node.name, isFolder, useTrash); 17 | const confirmMessage = useTrash ? "Move to Recycle Bin" : "Delete"; 18 | 19 | const answer: string | undefined = await window.showInformationMessage( 20 | message, 21 | { modal: true }, 22 | confirmMessage, 23 | ); 24 | 25 | if (answer === confirmMessage) { 26 | try { 27 | await workspace.fs.delete(Uri.parse(node.uri), { 28 | recursive: true, 29 | useTrash, 30 | }); 31 | } catch (e) { 32 | // See: https://github.com/microsoft/vscode-java-dependency/issues/608 33 | sendError(new Error("Failed to remove files.")); 34 | } 35 | } 36 | } 37 | 38 | function getInformationMessage(name: string, isFolder: boolean, useTrash: boolean): string { 39 | const folderMsg = isFolder ? " and its contents" : ""; 40 | let msg = `Are you sure you want to ${useTrash ? "" : "permanently "}delete \'${name}\'${folderMsg}?`; 41 | 42 | if (useTrash) { 43 | msg += "\n\nYou can restore from the Recycle Bin."; 44 | } 45 | return msg; 46 | } 47 | -------------------------------------------------------------------------------- /src/explorerCommands/rename.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as fse from "fs-extra"; 5 | import * as path from "path"; 6 | import { Uri, window, workspace, WorkspaceEdit } from "vscode"; 7 | import { NodeKind } from "../java/nodeData"; 8 | import { DataNode } from "../views/dataNode"; 9 | import { checkJavaQualifiedName, isMutable } from "./utility"; 10 | 11 | export async function renameFile(node?: DataNode): Promise { 12 | if (!node?.uri || !isMutable(node)) { 13 | return; 14 | } 15 | 16 | const oldFsPath = Uri.parse(node.uri).fsPath; 17 | 18 | const newName: string | undefined = await window.showInputBox({ 19 | placeHolder: "Input new file name", 20 | value: getPrefillValue(node), 21 | ignoreFocusOut: true, 22 | valueSelection: getValueSelection(node.uri), 23 | validateInput: async (value: string): Promise => { 24 | const checkMessage = CheckQualifiedInputName(value, node.nodeData.kind); 25 | if (checkMessage) { 26 | return checkMessage; 27 | } 28 | 29 | const inputFsPath = getRenamedFsPath(oldFsPath, value); 30 | if (await fse.pathExists(inputFsPath)) { 31 | return `File path: ${inputFsPath} already exists.`; 32 | } 33 | 34 | return ""; 35 | }, 36 | }); 37 | 38 | if (!newName) { 39 | return; 40 | } 41 | 42 | const newFsPath = getRenamedFsPath(oldFsPath, newName); 43 | const workspaceEdit: WorkspaceEdit = new WorkspaceEdit(); 44 | workspaceEdit.renameFile(Uri.file(oldFsPath), Uri.file(newFsPath)); 45 | workspace.applyEdit(workspaceEdit); 46 | } 47 | 48 | function getRenamedFsPath(oldUri: string, newName: string): string { 49 | // preserve default file extension if not provided 50 | if (!path.extname(newName)) { 51 | newName += path.extname(oldUri); 52 | } 53 | const dirname = path.dirname(oldUri); 54 | return path.join(dirname, newName); 55 | } 56 | 57 | function getPrefillValue(node: DataNode): string { 58 | const nodeKind = node.nodeData.kind; 59 | if (nodeKind === NodeKind.PrimaryType) { 60 | return node.name; 61 | } 62 | return path.basename(node.uri!); 63 | } 64 | 65 | function getValueSelection(uri: string): [number, number] | undefined { 66 | const pos = path.basename(uri).lastIndexOf("."); 67 | if (pos !== -1) { 68 | return [0, pos]; 69 | } 70 | return undefined; 71 | } 72 | 73 | function CheckQualifiedInputName(value: string, nodeKind: NodeKind): string { 74 | if (nodeKind === NodeKind.Folder || nodeKind === NodeKind.File) { 75 | return ""; 76 | } 77 | const javaValidateMessage = checkJavaQualifiedName(value); 78 | 79 | if (javaValidateMessage) { 80 | return javaValidateMessage; 81 | } 82 | 83 | if (nodeKind === NodeKind.Package || nodeKind === NodeKind.PackageRoot) { 84 | if (value.indexOf(".") !== -1) { 85 | return "Rename is only applicable to innermost package."; 86 | } 87 | } 88 | 89 | return ""; 90 | } 91 | -------------------------------------------------------------------------------- /src/explorerCommands/utility.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { isJavaIdentifier, isKeyword } from "../utility"; 5 | import { DataNode } from "../views/dataNode"; 6 | import { ExplorerNode } from "../views/explorerNode"; 7 | 8 | export function isMutable(node: DataNode): boolean { 9 | // avoid modify dependency files 10 | const packageExp = /java:(package|packageRoot)(?=.*?\b\+(source|resource)\b)(?=.*?\b\+uri\b)/; 11 | const resourceOrTypeExp = /java:(file|type|folder)(?=.*?\b\+uri\b)/; 12 | 13 | const contextValue = node.computeContextValue(); 14 | if (!contextValue) { 15 | return false; 16 | } 17 | return packageExp.test(contextValue) || resourceOrTypeExp.test(contextValue); 18 | } 19 | 20 | export function checkJavaQualifiedName(value: string): string { 21 | if (!value || !value.trim()) { 22 | return "Input cannot be empty."; 23 | } 24 | 25 | for (const part of value.split(".")) { 26 | if (isKeyword(part)) { 27 | return `Keyword '${part}' cannot be used.`; 28 | } 29 | 30 | if (!isJavaIdentifier(part)) { 31 | return `Invalid Java qualified name.`; 32 | } 33 | } 34 | 35 | return ""; 36 | } 37 | 38 | export function getCmdNode(selectedNodes: readonly ExplorerNode[], node?: DataNode): DataNode | undefined { 39 | // if command not invoked by context menu, use selected node in explorer 40 | if (node) { 41 | return node; 42 | } 43 | 44 | if (selectedNodes.length > 0) { 45 | return selectedNodes[0] as DataNode; 46 | } 47 | 48 | return undefined; 49 | } 50 | -------------------------------------------------------------------------------- /src/java/containerNodeData.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { INodeData } from "./nodeData"; 5 | 6 | export enum ContainerEntryKind { 7 | /** 8 | * Entry kind constant describing a classpath entry identifying a 9 | * library. A library is a folder or JAR containing package 10 | * fragments consisting of pre-compiled binaries. 11 | */ 12 | CPE_LIBRARY = 1, 13 | 14 | /** 15 | * Entry kind constant describing a classpath entry identifying a 16 | * required project. 17 | */ 18 | CPE_PROJECT = 2, 19 | 20 | /** 21 | * Entry kind constant describing a classpath entry identifying a 22 | * folder containing package fragments with source code 23 | * to be compiled. 24 | */ 25 | CPE_SOURCE = 3, 26 | 27 | /** 28 | * Entry kind constant describing a classpath entry defined using 29 | * a path that begins with a classpath variable reference. 30 | */ 31 | CPE_VARIABLE = 4, 32 | 33 | /** 34 | * Entry kind constant describing a classpath entry representing 35 | * a name classpath container. 36 | * 37 | * @since 2.0 38 | */ 39 | CPE_CONTAINER = 5, 40 | } 41 | 42 | export interface IContainerNodeData extends INodeData { 43 | entryKind: ContainerEntryKind; 44 | } 45 | -------------------------------------------------------------------------------- /src/java/hierarchicalPackageNodeData.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { INodeData, NodeKind } from "./nodeData"; 5 | 6 | export class HierarchicalPackageNodeData implements INodeData { 7 | 8 | public static createHierarchicalNodeDataByPackageList(packageList: INodeData[]): HierarchicalPackageNodeData { 9 | const result = new HierarchicalPackageNodeData("", ""); 10 | packageList.forEach((nodeData) => result.addSubPackage(nodeData.name.split("."), nodeData)); 11 | result.compressTree(); 12 | return result; 13 | } 14 | 15 | public name: string; 16 | public children: HierarchicalPackageNodeData[] = []; 17 | public displayName: string; 18 | private nodeData?: INodeData; 19 | 20 | public get uri() { 21 | return this.nodeData && this.nodeData.uri; 22 | } 23 | 24 | public get moduleName() { 25 | return this.nodeData && this.nodeData.moduleName; 26 | } 27 | 28 | public get path() { 29 | return this.nodeData && this.nodeData.path; 30 | } 31 | 32 | public get kind() { 33 | return this.nodeData ? this.nodeData.kind : NodeKind.Package; 34 | } 35 | 36 | public get isPackage() { 37 | return this.nodeData !== undefined; 38 | } 39 | 40 | public get handlerIdentifier() { 41 | return this.nodeData?.handlerIdentifier; 42 | } 43 | 44 | private constructor(displayName: string, parentName: string) { 45 | this.displayName = displayName; 46 | this.name = parentName === "" ? displayName : parentName + "." + displayName; 47 | } 48 | 49 | private compressTree(): void { 50 | // Don't compress the root node 51 | while (this.name !== "" && this.children.length === 1 && !this.isPackage) { 52 | const child = this.children[0]; 53 | this.name = this.name + "." + child.displayName; 54 | this.displayName = this.displayName + "." + child.displayName; 55 | this.children = child.children; 56 | this.nodeData = child.nodeData; 57 | } 58 | this.children.forEach((child) => child.compressTree()); 59 | } 60 | 61 | private addSubPackage(packages: string[], nodeData: INodeData): void { 62 | if (!packages.length) { 63 | this.nodeData = nodeData; 64 | return; 65 | } 66 | const subPackageDisplayName = packages.shift(); 67 | const childNode = this.children.find((child) => child.displayName === subPackageDisplayName); 68 | if (childNode) { 69 | childNode.addSubPackage(packages, nodeData); 70 | } else { 71 | const newNode = new HierarchicalPackageNodeData(subPackageDisplayName as string, this.name); 72 | newNode.addSubPackage(packages, nodeData); 73 | this.children.push(newNode); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/java/jdtls.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | 5 | import * as minimatch from "minimatch"; 6 | import { CancellationToken, Uri, commands, workspace } from "vscode"; 7 | import { Commands, executeJavaLanguageServerCommand } from "../commands"; 8 | import { IClasspath } from "../tasks/buildArtifact/IStepMetadata"; 9 | import { IMainClassInfo } from "../tasks/buildArtifact/ResolveMainClassExecutor"; 10 | import { INodeData, NodeKind } from "./nodeData"; 11 | import { Settings } from "../settings"; 12 | 13 | export namespace Jdtls { 14 | export async function getProjects(params: string): Promise { 15 | return await commands.executeCommand( 16 | Commands.EXECUTE_WORKSPACE_COMMAND, 17 | Commands.JAVA_PROJECT_LIST, 18 | params, 19 | Settings.nonJavaResourcesFiltered() 20 | ) || []; 21 | } 22 | 23 | export async function getProjectUris(): Promise { 24 | return await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.GET_ALL_PROJECTS) || []; 25 | } 26 | 27 | export async function refreshLibraries(params: string): Promise { 28 | return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_REFRESH_LIB_SERVER, params); 29 | } 30 | 31 | export async function getPackageData(params: IPackageDataParam): Promise { 32 | const uri: Uri | null = !params.projectUri ? null : Uri.parse(params.projectUri); 33 | const excludePatterns: {[key: string]: boolean} | undefined = workspace.getConfiguration("files", uri).get("exclude"); 34 | 35 | let nodeData: INodeData[] = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, 36 | Commands.JAVA_GETPACKAGEDATA, params) || []; 37 | 38 | // check filter settings. 39 | if (Settings.nonJavaResourcesFiltered()) { 40 | nodeData = nodeData.filter((data: INodeData) => { 41 | return data.kind !== NodeKind.Folder && data.kind !== NodeKind.File; 42 | }); 43 | } 44 | 45 | if (excludePatterns && nodeData.length) { 46 | const uriOfChildren: string[] = nodeData.map((node: INodeData) => node.uri).filter(Boolean) as string[]; 47 | const urisToExclude: Set = new Set(); 48 | for (const pattern in excludePatterns) { 49 | if (excludePatterns[pattern]) { 50 | const toExclude: string[] = minimatch.match(uriOfChildren, pattern); 51 | toExclude.forEach((uriToExclude: string) => urisToExclude.add(uriToExclude)); 52 | } 53 | } 54 | 55 | if (urisToExclude.size) { 56 | nodeData = nodeData.filter((node: INodeData) => { 57 | if (!node.uri) { 58 | return true; 59 | } 60 | return !urisToExclude.has(node.uri); 61 | }); 62 | } 63 | } 64 | return nodeData; 65 | } 66 | 67 | export async function resolvePath(params: string): Promise { 68 | return await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_RESOLVEPATH, params) || []; 69 | } 70 | 71 | export async function getMainClasses(params: string): Promise { 72 | return await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_GETMAINCLASSES, params) || []; 73 | } 74 | 75 | export async function exportJar(mainClass: string, classpaths: IClasspath[], 76 | destination: string, terminalId: string, token: CancellationToken): Promise { 77 | return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_GENERATEJAR, 78 | mainClass, classpaths, destination, terminalId, token); 79 | } 80 | 81 | export async function checkImportStatus(): Promise { 82 | return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_CHECK_IMPORT_STATUS) || false; 83 | } 84 | 85 | export enum CompileWorkspaceStatus { 86 | Failed = 0, 87 | Succeed = 1, 88 | Witherror = 2, 89 | Cancelled = 3, 90 | } 91 | 92 | export function resolveBuildFiles(): Promise { 93 | return >executeJavaLanguageServerCommand(Commands.JAVA_RESOLVE_BUILD_FILES); 94 | } 95 | } 96 | 97 | interface IPackageDataParam { 98 | projectUri: string | undefined; 99 | [key: string]: any; 100 | } -------------------------------------------------------------------------------- /src/java/nodeData.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | export enum NodeKind { 5 | Workspace = 1, 6 | Project = 2, 7 | PackageRoot = 3, 8 | Package = 4, 9 | PrimaryType = 5, 10 | CompilationUnit = 6, 11 | ClassFile = 7, 12 | Container = 8, 13 | Folder = 9, 14 | File = 10, 15 | } 16 | 17 | export enum TypeKind { 18 | Class = 1, 19 | Interface = 2, 20 | Enum = 3, 21 | } 22 | 23 | export interface INodeData { 24 | displayName?: string; 25 | name: string; 26 | moduleName?: string; 27 | path?: string; 28 | /** 29 | * returned from Java side using `IJavaElement.getHandlerIdentifier();` 30 | */ 31 | handlerIdentifier?: string; 32 | uri?: string; 33 | kind: NodeKind; 34 | children?: any[]; 35 | metaData?: { [id: string]: any }; 36 | } 37 | -------------------------------------------------------------------------------- /src/java/packageRootNodeData.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { INodeData } from "./nodeData"; 5 | 6 | export enum PackageRootKind { 7 | /** 8 | * Kind constant for a source path root. Indicates this root 9 | * only contains source files. 10 | */ 11 | K_SOURCE = 1, 12 | /** 13 | * Kind constant for a binary path root. Indicates this 14 | * root only contains binary files. 15 | */ 16 | K_BINARY = 2, 17 | } 18 | 19 | export interface IPackageRootNodeData extends INodeData { 20 | entryKind: PackageRootKind; 21 | 22 | attributes: Map; 23 | } 24 | -------------------------------------------------------------------------------- /src/java/typeRootNodeData.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { SymbolInformation } from "vscode"; 5 | import { INodeData } from "./nodeData"; 6 | 7 | export interface ITypeRootNodeData extends INodeData { 8 | symbolTree?: Map; 9 | } 10 | -------------------------------------------------------------------------------- /src/languageServerApi/LanguageServerMode.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | export const enum LanguageServerMode { 5 | LightWeight = "LightWeight", 6 | Standard = "Standard", 7 | Hybrid = "Hybrid", 8 | } 9 | -------------------------------------------------------------------------------- /src/languageServerApi/languageServerApiManager.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { commands, Event, Extension, extensions, Uri, window } from "vscode"; 5 | import { Commands } from "../commands"; 6 | import { Context, ExtensionName } from "../constants"; 7 | import { contextManager } from "../contextManager"; 8 | import { Settings } from "../settings"; 9 | import { syncHandler } from "../syncHandler"; 10 | import { LanguageServerMode } from "./LanguageServerMode"; 11 | 12 | class LanguageServerApiManager { 13 | private extensionApi: any; 14 | 15 | private isServerReady: boolean = false; 16 | 17 | public async ready(): Promise { 18 | if (this.isServerReady) { 19 | return true; 20 | } 21 | 22 | if (!this.isApiInitialized()) { 23 | await this.initializeJavaLanguageServerApis(); 24 | } 25 | 26 | const serverMode: LanguageServerMode | undefined = this.extensionApi?.serverMode; 27 | if (!serverMode || serverMode === LanguageServerMode.LightWeight) { 28 | return false; 29 | } 30 | 31 | await this.extensionApi.serverReady(); 32 | this.isServerReady = true; 33 | return true; 34 | } 35 | 36 | public async initializeJavaLanguageServerApis(): Promise { 37 | if (this.isApiInitialized()) { 38 | return; 39 | } 40 | 41 | const extension: Extension | undefined = extensions.getExtension(ExtensionName.JAVA_LANGUAGE_SUPPORT); 42 | if (extension) { 43 | contextManager.setContextValue(Context.LANGUAGE_SUPPORT_INSTALLED, true); 44 | await extension.activate(); 45 | const extensionApi: any = extension.exports; 46 | if (!extensionApi) { 47 | window.showErrorMessage("Please update 'redhat.java' to the latest version."); 48 | return; 49 | } 50 | 51 | this.extensionApi = extensionApi; 52 | if (extensionApi.onDidClasspathUpdate) { 53 | const onDidClasspathUpdate: Event = extensionApi.onDidClasspathUpdate; 54 | contextManager.context.subscriptions.push(onDidClasspathUpdate(() => { 55 | commands.executeCommand(Commands.VIEW_PACKAGE_INTERNAL_REFRESH, /* debounce = */true); 56 | syncHandler.updateFileWatcher(Settings.autoRefresh()); 57 | })); 58 | } 59 | 60 | if (extensionApi.onDidProjectsImport) { 61 | const onDidProjectsImport: Event = extensionApi.onDidProjectsImport; 62 | contextManager.context.subscriptions.push(onDidProjectsImport(() => { 63 | commands.executeCommand(Commands.VIEW_PACKAGE_INTERNAL_REFRESH, /* debounce = */true); 64 | syncHandler.updateFileWatcher(Settings.autoRefresh()); 65 | })); 66 | } 67 | 68 | if (extensionApi.onDidProjectsDelete) { 69 | const onDidProjectsDelete: Event = extensionApi.onDidProjectsDelete; 70 | contextManager.context.subscriptions.push(onDidProjectsDelete(() => { 71 | commands.executeCommand(Commands.VIEW_PACKAGE_INTERNAL_REFRESH, /* debounce = */true); 72 | syncHandler.updateFileWatcher(Settings.autoRefresh()); 73 | })); 74 | 75 | } 76 | 77 | if (this.extensionApi?.serverMode === LanguageServerMode.LightWeight) { 78 | if (extensionApi.onDidServerModeChange) { 79 | const onDidServerModeChange: Event = extensionApi.onDidServerModeChange; 80 | contextManager.context.subscriptions.push(onDidServerModeChange((mode: LanguageServerMode) => { 81 | if (mode === LanguageServerMode.Hybrid) { 82 | commands.executeCommand(Commands.VIEW_PACKAGE_INTERNAL_REFRESH, /* debounce = */false); 83 | } 84 | })); 85 | } 86 | } 87 | } 88 | } 89 | 90 | private isApiInitialized(): boolean { 91 | return this.extensionApi !== undefined; 92 | } 93 | 94 | /** 95 | * Check if the language server is ready in the given timeout. 96 | * @param timeout the timeout in milliseconds to wait 97 | * @returns false if the language server is not ready in the given timeout, otherwise true 98 | */ 99 | public isReady(timeout: number): Promise { 100 | return Promise.race([this.ready(), new Promise((resolve) => setTimeout(() => resolve(false), timeout))]); 101 | } 102 | } 103 | 104 | export const languageServerApiManager: LanguageServerApiManager = new LanguageServerApiManager(); 105 | -------------------------------------------------------------------------------- /src/tasks/buildArtifact/IExportJarStepExecutor.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { IStepMetadata } from "./IStepMetadata"; 5 | 6 | export interface IExportJarStepExecutor { 7 | execute(stepMetadata?: IStepMetadata): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /src/tasks/buildArtifact/IStepMetadata.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { WorkspaceFolder } from "vscode"; 5 | import { INodeData } from "../../java/nodeData"; 6 | import { ExportJarStep } from "./utility"; 7 | 8 | export interface IStepMetadata { 9 | entry?: INodeData; 10 | taskLabel: string; 11 | terminalId?: string; 12 | workspaceFolder?: WorkspaceFolder; 13 | mainClass?: string; 14 | outputPath?: string; 15 | projectList: INodeData[]; 16 | elements: string[]; 17 | classpaths: IClasspath[]; 18 | steps: ExportJarStep[]; 19 | } 20 | 21 | export interface IClasspath { 22 | source: string; 23 | destination: string | undefined; 24 | isArtifact: boolean; 25 | } 26 | -------------------------------------------------------------------------------- /src/tasks/buildArtifact/ResolveJavaProjectExecutor.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as _ from "lodash"; 5 | import { Disposable, QuickPickItem, Uri, workspace, WorkspaceFolder } from "vscode"; 6 | import { Jdtls } from "../../java/jdtls"; 7 | import { INodeData } from "../../java/nodeData"; 8 | import { WorkspaceNode } from "../../views/workspaceNode"; 9 | import { IExportJarStepExecutor } from "./IExportJarStepExecutor"; 10 | import { IStepMetadata } from "./IStepMetadata"; 11 | import { createPickBox, ExportJarMessages, ExportJarStep } from "./utility"; 12 | 13 | export class ResolveJavaProjectExecutor implements IExportJarStepExecutor { 14 | 15 | private readonly currentStep: ExportJarStep = ExportJarStep.ResolveJavaProject; 16 | 17 | public async execute(stepMetadata: IStepMetadata): Promise { 18 | if (stepMetadata.workspaceFolder === undefined) { 19 | await this.resolveJavaProject(stepMetadata); 20 | } 21 | return true; 22 | } 23 | 24 | private async resolveJavaProject(stepMetadata: IStepMetadata): Promise { 25 | // Guarded by workspaceFolderCount != 0 in package.json 26 | const folders = workspace.workspaceFolders!; 27 | if (stepMetadata.entry instanceof WorkspaceNode) { 28 | if (!stepMetadata.entry?.uri) { 29 | throw new Error(ExportJarMessages.fieldUndefinedMessage(ExportJarMessages.Field.ENTRY, this.currentStep)); 30 | } 31 | const workspaceUri: Uri = Uri.parse(stepMetadata.entry.uri); 32 | for (const folder of folders) { 33 | if (folder.uri.toString() === workspaceUri.toString()) { 34 | stepMetadata.workspaceFolder = folder; 35 | } 36 | } 37 | stepMetadata.projectList = await Jdtls.getProjects(workspaceUri.toString()); 38 | return; 39 | } 40 | if (folders.length === 1) { 41 | stepMetadata.workspaceFolder = folders[0]; 42 | stepMetadata.projectList = await Jdtls.getProjects(folders[0].uri.toString()); 43 | return; 44 | } 45 | const pickItems: IJavaProjectQuickPickItem[] = []; 46 | const projectMap: Map = new Map(); 47 | for (const folder of folders) { 48 | const projects: INodeData[] = await Jdtls.getProjects(folder.uri.toString()); 49 | if (!_.isEmpty(projects)) { 50 | pickItems.push({ 51 | label: folder.name, 52 | description: folder.uri.fsPath, 53 | workspaceFolder: folder, 54 | }); 55 | projectMap.set(folder.uri.toString(), projects); 56 | } 57 | } 58 | if (_.isEmpty(pickItems)) { 59 | throw new Error(ExportJarMessages.JAVAWORKSPACES_EMPTY); 60 | } 61 | const disposables: Disposable[] = []; 62 | try { 63 | await new Promise((resolve, reject) => { 64 | const pickBox = createPickBox("Export Jar : Determine workspace", 65 | "Select the workspace", pickItems, false); 66 | disposables.push( 67 | pickBox.onDidAccept(() => { 68 | if (_.isEmpty(pickBox.selectedItems)) { 69 | return; 70 | } 71 | const projectList: INodeData[] = projectMap.get(pickBox.selectedItems[0].workspaceFolder.uri.toString()) || []; 72 | if (_.isEmpty(projectList)) { 73 | return reject(new Error(ExportJarMessages.WORKSPACE_EMPTY)); 74 | } 75 | stepMetadata.projectList = projectList; 76 | stepMetadata.workspaceFolder = pickBox.selectedItems[0].workspaceFolder; 77 | stepMetadata.steps.push(ExportJarStep.ResolveJavaProject); 78 | return resolve(); 79 | }), 80 | pickBox.onDidHide(() => { 81 | return reject(); 82 | }), 83 | ); 84 | disposables.push(pickBox); 85 | pickBox.show(); 86 | }); 87 | } finally { 88 | for (const d of disposables) { 89 | d.dispose(); 90 | } 91 | } 92 | } 93 | } 94 | 95 | interface IJavaProjectQuickPickItem extends QuickPickItem { 96 | workspaceFolder: WorkspaceFolder; 97 | } 98 | -------------------------------------------------------------------------------- /src/tasks/buildArtifact/ResolveMainClassExecutor.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as _ from "lodash"; 5 | import { Disposable, ProgressLocation, QuickInputButtons, QuickPickItem, window } from "vscode"; 6 | import { Jdtls } from "../../java/jdtls"; 7 | import { IExportJarStepExecutor } from "./IExportJarStepExecutor"; 8 | import { IStepMetadata } from "./IStepMetadata"; 9 | import { createPickBox, ExportJarMessages, ExportJarStep } from "./utility"; 10 | 11 | export class ResolveMainClassExecutor implements IExportJarStepExecutor { 12 | 13 | private static getName(data: IMainClassInfo) { 14 | return data.name.substring(data.name.lastIndexOf(".") + 1); 15 | } 16 | 17 | private readonly currentStep: ExportJarStep = ExportJarStep.ResolveMainClass; 18 | 19 | public async execute(stepMetadata: IStepMetadata): Promise { 20 | if (stepMetadata.mainClass !== undefined) { 21 | return true; 22 | } 23 | return this.resolveMainClass(stepMetadata); 24 | } 25 | 26 | private async resolveMainClass(stepMetadata: IStepMetadata): Promise { 27 | const mainClasses: IMainClassInfo[] | undefined = await window.withProgress({ 28 | location: ProgressLocation.Window, 29 | title: "Exporting Jar : Resolving main classes...", 30 | cancellable: true, 31 | }, (_progress, token) => { 32 | return new Promise(async (resolve, reject) => { 33 | token.onCancellationRequested(() => { 34 | return reject(); 35 | }); 36 | if (!stepMetadata.workspaceFolder) { 37 | return reject(new Error(ExportJarMessages.fieldUndefinedMessage(ExportJarMessages.Field.WORKSPACEFOLDER, this.currentStep))); 38 | } 39 | resolve(await Jdtls.getMainClasses(stepMetadata.workspaceFolder.uri.toString())); 40 | }); 41 | }); 42 | if (_.isEmpty(mainClasses)) { 43 | stepMetadata.mainClass = ""; 44 | return true; 45 | } 46 | const pickItems: QuickPickItem[] = []; 47 | for (const mainClass of mainClasses) { 48 | pickItems.push({ 49 | label: ResolveMainClassExecutor.getName(mainClass), 50 | description: mainClass.name, 51 | }); 52 | } 53 | const noMainClassItem: QuickPickItem = { 54 | label: "", 55 | description: "", 56 | }; 57 | pickItems.push(noMainClassItem); 58 | const disposables: Disposable[] = []; 59 | let result: boolean = false; 60 | try { 61 | result = await new Promise(async (resolve, reject) => { 62 | const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class", 63 | pickItems, stepMetadata.steps.length > 0); 64 | disposables.push( 65 | pickBox.onDidTriggerButton((item) => { 66 | if (item === QuickInputButtons.Back) { 67 | return resolve(false); 68 | } 69 | }), 70 | pickBox.onDidAccept(() => { 71 | if (_.isEmpty(pickBox.selectedItems)) { 72 | return; 73 | } 74 | stepMetadata.mainClass = pickBox.selectedItems[0].description; 75 | stepMetadata.steps.push(ExportJarStep.ResolveMainClass); 76 | return resolve(true); 77 | }), 78 | pickBox.onDidHide(() => { 79 | return reject(); 80 | }), 81 | ); 82 | disposables.push(pickBox); 83 | pickBox.show(); 84 | }); 85 | } finally { 86 | for (const d of disposables) { 87 | d.dispose(); 88 | } 89 | } 90 | return result; 91 | } 92 | } 93 | 94 | export interface IMainClassInfo { 95 | name: string; 96 | path: string; 97 | } 98 | -------------------------------------------------------------------------------- /src/tasks/buildArtifact/migration/CodeActionProvider.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as vscode from "vscode"; 5 | import { Commands } from "../../../commands"; 6 | import { BuildArtifactTaskProvider } from "../BuildArtifactTaskProvider"; 7 | import { DiagnosticProvider } from "./DiagnosticProvider"; 8 | 9 | export class CodeActionProvider implements vscode.CodeActionProvider { 10 | 11 | public static JAVA_UPDATE_DEPRECATED_TASK_TITLE = `Change to \"${BuildArtifactTaskProvider.exportJarType}\"`; 12 | public static JAVA_BUILD_ARTIFACT_TYPE = `"type": "${BuildArtifactTaskProvider.exportJarType}"`; 13 | 14 | public provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, 15 | _context: vscode.CodeActionContext, _token: vscode.CancellationToken): vscode.CodeAction[] | undefined { 16 | const diagnostics = vscode.languages.getDiagnostics(document.uri); 17 | if (diagnostics?.length) { 18 | for (const diagnostic of diagnostics) { 19 | if (diagnostic.source !== DiagnosticProvider.DIAGNOSTIC_SOURCE) { 20 | continue; 21 | } 22 | if (diagnostic.range.contains(range)) { 23 | const updateTaskCommand: vscode.Command = { 24 | command: Commands.JAVA_UPDATE_DEPRECATED_TASK, 25 | title: CodeActionProvider.JAVA_UPDATE_DEPRECATED_TASK_TITLE, 26 | arguments: [ 27 | document, 28 | diagnostic.range 29 | ] 30 | }; 31 | return [{ 32 | title: CodeActionProvider.JAVA_UPDATE_DEPRECATED_TASK_TITLE, 33 | kind: vscode.CodeActionKind.QuickFix, 34 | command: updateTaskCommand 35 | }]; 36 | } 37 | } 38 | } 39 | return []; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/tasks/buildArtifact/migration/DiagnosticProvider.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as _ from "lodash"; 5 | import * as fs from "fs"; 6 | import * as path from "path"; 7 | import * as readline from "readline"; 8 | import * as vscode from "vscode"; 9 | import { DeprecatedExportJarTaskProvider, BuildArtifactTaskProvider } from "../BuildArtifactTaskProvider"; 10 | 11 | export class DiagnosticProvider implements vscode.Disposable { 12 | public static DIAGNOSTIC_SOURCE = "Project Manager for Java"; 13 | public static DEPRECATED_TASK_TYPE_MESSAGE = `The type \"${DeprecatedExportJarTaskProvider.type}\" is deprecated, please use \"${BuildArtifactTaskProvider.exportJarType}\" instead.`; 14 | private diagnosticCollection: vscode.DiagnosticCollection; 15 | private disposables: vscode.Disposable[] = []; 16 | private refreshDiagnosticsTrigger: any; 17 | 18 | constructor() { 19 | this.refreshDiagnosticsTrigger = _.debounce(this.refreshDiagnostics, 500 /** ms */); 20 | this.diagnosticCollection = vscode.languages.createDiagnosticCollection("migrateExportTask"); 21 | this.disposables.push(this.diagnosticCollection); 22 | this.disposables.push(vscode.workspace.onDidChangeTextDocument(async (e: vscode.TextDocumentChangeEvent) => { 23 | if (path.basename(e.document.fileName) === "tasks.json") { 24 | this.refreshDiagnosticsTrigger(e.document.uri); 25 | } 26 | })); 27 | this.disposables.push(vscode.workspace.onDidOpenTextDocument(async (e: vscode.TextDocument) => { 28 | if (path.basename(e.fileName) === "tasks.json") { 29 | this.refreshDiagnosticsTrigger(e.uri); 30 | } 31 | })); 32 | this.disposables.push(vscode.workspace.onDidCloseTextDocument(async (e: vscode.TextDocument) => { 33 | if (path.basename(e.fileName) === "tasks.json") { 34 | this.diagnosticCollection.set(e.uri, undefined); 35 | } 36 | })); 37 | } 38 | 39 | public dispose() { 40 | for (const d of this.disposables) { 41 | d.dispose(); 42 | } 43 | } 44 | 45 | private async refreshDiagnostics(uri: vscode.Uri): Promise { 46 | const diagnostics: vscode.Diagnostic[] = await DiagnosticProvider.getDiagnosticsFromTasksJsonPath(uri.fsPath); 47 | this.diagnosticCollection.set(uri, diagnostics); 48 | } 49 | 50 | private static async getDiagnosticsFromTasksJsonPath(tasksJsonPath: string): Promise { 51 | const diagnostics: vscode.Diagnostic[] = []; 52 | const fileStream = fs.createReadStream(tasksJsonPath); 53 | let lineNumber = 0; 54 | const rl = readline.createInterface({ 55 | input: fileStream, 56 | crlfDelay: Infinity 57 | }); 58 | for await (const line of rl) { 59 | const regExp: RegExp = /\"type\":\s*\"java\"/g; 60 | const result: RegExpMatchArray | null = line.match(regExp); 61 | if (result?.length === 1) { 62 | const matchString = result[0]; 63 | const columnNumber = line.indexOf(matchString); 64 | if (columnNumber > -1) { 65 | const diagnostic = new vscode.Diagnostic( 66 | new vscode.Range( 67 | new vscode.Position(lineNumber, columnNumber), 68 | new vscode.Position(lineNumber, columnNumber + matchString.length) 69 | ), 70 | DiagnosticProvider.DEPRECATED_TASK_TYPE_MESSAGE, 71 | vscode.DiagnosticSeverity.Warning 72 | ); 73 | diagnostic.source = DiagnosticProvider.DIAGNOSTIC_SOURCE; 74 | diagnostics.push(diagnostic); 75 | } 76 | } 77 | lineNumber++; 78 | } 79 | return diagnostics; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/tasks/buildArtifact/migration/utils.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { Range, tasks, TextDocument, workspace, WorkspaceEdit } from "vscode"; 5 | import { DeprecatedExportJarTaskProvider } from "../BuildArtifactTaskProvider"; 6 | import { contextManager } from "../../../contextManager"; 7 | import { Context } from "../../../constants"; 8 | import { CodeActionProvider } from "./CodeActionProvider"; 9 | 10 | export async function updateExportTaskType(document: TextDocument, range: Range): Promise { 11 | const workspaceEdit = new WorkspaceEdit(); 12 | workspaceEdit.replace(document.uri, range, CodeActionProvider.JAVA_BUILD_ARTIFACT_TYPE); 13 | await workspace.applyEdit(workspaceEdit); 14 | await document.save(); 15 | } 16 | 17 | export async function setContextForDeprecatedTasks(): Promise { 18 | await contextManager.setContextValue(Context.SHOW_DEPRECATED_TASKS, true); 19 | const deprecatedTasks = await tasks.fetchTasks({ type: DeprecatedExportJarTaskProvider.type }); 20 | if (deprecatedTasks?.length) { 21 | return; 22 | } 23 | await contextManager.setContextValue(Context.SHOW_DEPRECATED_TASKS, false); 24 | } 25 | -------------------------------------------------------------------------------- /src/utility.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { Uri, window, workspace, WorkspaceFolder } from "vscode"; 5 | import { setUserError } from "vscode-extension-telemetry-wrapper"; 6 | import { INodeData } from "./java/nodeData"; 7 | import { languageServerApiManager } from "./languageServerApi/languageServerApiManager"; 8 | 9 | export class Utility { 10 | 11 | public static getDefaultWorkspaceFolder(): WorkspaceFolder | undefined { 12 | if (workspace.workspaceFolders === undefined) { 13 | return undefined; 14 | } 15 | if (workspace.workspaceFolders.length === 1) { 16 | return workspace.workspaceFolders[0]; 17 | } 18 | if (window.activeTextEditor) { 19 | const activeWorkspaceFolder: WorkspaceFolder | undefined = 20 | workspace.getWorkspaceFolder(window.activeTextEditor.document.uri); 21 | return activeWorkspaceFolder; 22 | } 23 | return undefined; 24 | } 25 | 26 | public static async isRevealable(uri: Uri): Promise { 27 | if (!SUPPORTED_URI_SCHEMES.includes(uri.scheme)) { 28 | return false; 29 | } 30 | if (uri.scheme === "file" && !workspace.getWorkspaceFolder(uri)) { 31 | return false; 32 | } 33 | 34 | return languageServerApiManager.ready(); 35 | } 36 | } 37 | 38 | export class EventCounter { 39 | public static dict: {[key: string]: number} = {}; 40 | 41 | public static increase(event: string) { 42 | const count = this.dict[event] ?? 0; 43 | this.dict[event] = count + 1; 44 | } 45 | } 46 | 47 | export class UserError extends Error { 48 | public context: ITroubleshootingMessage; 49 | 50 | constructor(context: ITroubleshootingMessage) { 51 | super(context.message); 52 | this.context = context; 53 | setUserError(this); 54 | } 55 | } 56 | 57 | interface IProperties { 58 | [key: string]: string; 59 | } 60 | 61 | interface ILoggingMessage { 62 | message: string; 63 | type?: Type; 64 | details?: IProperties; 65 | } 66 | 67 | interface ITroubleshootingMessage extends ILoggingMessage { 68 | anchor?: string; 69 | } 70 | 71 | export enum Type { 72 | EXCEPTION = "exception", 73 | USAGEDATA = "usageData", 74 | USAGEERROR = "usageError", 75 | ACTIVATEEXTENSION = "activateExtension", // TODO: Activation belongs to usage data, remove this category. 76 | } 77 | 78 | const keywords: Set = new Set([ 79 | "abstract", "default", "if", "private", "this", "boolean", "do", "implements", "protected", "throw", "break", "double", "import", 80 | "public", "throws", "byte", "else", "instanceof", "return", "transient", "case", "extends", "int", "short", "try", "catch", "final", 81 | "interface", "static", "void", "char", "finally", "long", "strictfp", "volatile", "class", "float", "native", "super", "while", 82 | "const", "for", "new", "switch", "continue", "goto", "package", "synchronized", "true", "false", "null", "assert", "enum", 83 | ]); 84 | 85 | const SUPPORTED_URI_SCHEMES: string[] = ["file", "jdt"]; 86 | 87 | export function isKeyword(identifier: string): boolean { 88 | return keywords.has(identifier); 89 | } 90 | 91 | const identifierRegExp: RegExp = /^([a-zA-Z_$][a-zA-Z\d_$]*)$/; 92 | export function isJavaIdentifier(identifier: string): boolean { 93 | return identifierRegExp.test(identifier); 94 | } 95 | 96 | export function isTest(nodeData: INodeData | undefined): boolean { 97 | if (!nodeData) { 98 | return false; 99 | } 100 | 101 | if (nodeData.metaData?.test === "true") { 102 | return true; 103 | } 104 | 105 | const mavenScope: string = nodeData.metaData?.["maven.scope"] || ""; 106 | if (mavenScope.toLocaleLowerCase().includes("test")) { 107 | return true; 108 | } 109 | 110 | const gradleScope: string = nodeData.metaData?.gradle_scope || ""; 111 | if (gradleScope.toLocaleLowerCase().includes("test")) { 112 | return true; 113 | } 114 | 115 | return false; 116 | } 117 | -------------------------------------------------------------------------------- /src/utils/Lock.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import AwaitLock from "await-lock"; 5 | 6 | export const explorerLock: AwaitLock = new AwaitLock(); 7 | -------------------------------------------------------------------------------- /src/views/containerNode.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { ThemeIcon, Uri } from "vscode"; 5 | import { Explorer } from "../constants"; 6 | import { Jdtls } from "../java/jdtls"; 7 | import { INodeData, NodeKind } from "../java/nodeData"; 8 | import { DataNode } from "./dataNode"; 9 | import { ExplorerNode } from "./explorerNode"; 10 | import { NodeFactory } from "./nodeFactory"; 11 | import { ProjectNode } from "./projectNode"; 12 | 13 | export class ContainerNode extends DataNode { 14 | constructor(nodeData: INodeData, parent: DataNode, private readonly _project: ProjectNode) { 15 | super(nodeData, parent); 16 | } 17 | 18 | private _containerType: ContainerType; 19 | 20 | public get projectBasePath() { 21 | return this._project.uri && Uri.parse(this._project.uri).fsPath; 22 | } 23 | 24 | public getContainerType(): ContainerType { 25 | if (this._containerType) { 26 | return this._containerType; 27 | } 28 | 29 | const containerPath: string = this._nodeData.path || ""; 30 | if (containerPath.startsWith(ContainerPath.JRE)) { 31 | this._containerType = ContainerType.JRE; 32 | } else if (containerPath.startsWith(ContainerPath.Maven)) { 33 | this._containerType = ContainerType.Maven; 34 | } else if (containerPath.startsWith(ContainerPath.Gradle)) { 35 | this._containerType = ContainerType.Gradle; 36 | } else if (containerPath.startsWith(ContainerPath.ReferencedLibrary) && this._project.isUnmanagedFolder()) { 37 | // currently, we only support editing referenced libraries in unmanaged folders 38 | this._containerType = ContainerType.ReferencedLibrary; 39 | } else { 40 | this._containerType = ContainerType.Unknown; 41 | } 42 | 43 | return this._containerType; 44 | } 45 | 46 | public isMavenType(): boolean { 47 | return this._containerType === ContainerType.Maven; 48 | } 49 | 50 | protected async loadData(): Promise { 51 | return Jdtls.getPackageData({ kind: NodeKind.Container, projectUri: this._project.uri, path: this.path }); 52 | } 53 | 54 | protected createChildNodeList(): ExplorerNode[] { 55 | const result: (ExplorerNode | undefined)[] = []; 56 | if (this.nodeData.children && this.nodeData.children.length) { 57 | this.nodeData.children.forEach((nodeData) => { 58 | result.push(NodeFactory.createNode(nodeData, this, this._project)); 59 | }); 60 | } 61 | return result.filter((n?: T): n is T => Boolean(n)); 62 | } 63 | 64 | protected get contextValue(): string { 65 | let contextValue: string = Explorer.ContextValueType.Container; 66 | const containerType: string = this.getContainerType(); 67 | if (containerType) { 68 | contextValue += `+${containerType}`; 69 | } 70 | return contextValue; 71 | } 72 | 73 | protected get iconPath(): ThemeIcon { 74 | return new ThemeIcon("folder-library"); 75 | } 76 | } 77 | 78 | export enum ContainerType { 79 | JRE = "jre", 80 | Maven = "maven", 81 | Gradle = "gradle", 82 | ReferencedLibrary = "referencedLibrary", 83 | Unknown = "", 84 | } 85 | 86 | const enum ContainerPath { 87 | JRE = "org.eclipse.jdt.launching.JRE_CONTAINER", 88 | Maven = "org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER", 89 | Gradle = "org.eclipse.buildship.core.gradleclasspathcontainer", 90 | ReferencedLibrary = "REFERENCED_LIBRARIES_PATH", 91 | } 92 | -------------------------------------------------------------------------------- /src/views/documentSymbolNode.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { Command, DocumentSymbol, Range, SymbolKind, ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode"; 5 | import { Commands } from "../commands"; 6 | import { Explorer } from "../constants"; 7 | import { ExplorerNode } from "./explorerNode"; 8 | import { PrimaryTypeNode } from "./PrimaryTypeNode"; 9 | 10 | export class DocumentSymbolNode extends ExplorerNode { 11 | 12 | private readonly _iconMap: Map = new Map([ 13 | [SymbolKind.Package, "package"], 14 | [SymbolKind.Class, "class"], 15 | [SymbolKind.Interface, "interface"], 16 | [SymbolKind.Enum, "enum"], 17 | [SymbolKind.EnumMember, "enum-member"], 18 | [SymbolKind.Constant, "constant"], 19 | [SymbolKind.Method, "method"], 20 | [SymbolKind.Function, "method"], 21 | [SymbolKind.Constructor, "method"], 22 | [SymbolKind.Field, "field"], 23 | [SymbolKind.Property, "property"], 24 | [SymbolKind.Variable, "variable"], 25 | ]); 26 | 27 | constructor(private readonly symbolInfo: DocumentSymbol, parent: PrimaryTypeNode) { 28 | super(parent); 29 | } 30 | 31 | public getDisplayName(): string { 32 | return this.symbolInfo.name; 33 | } 34 | 35 | public getChildren(): ExplorerNode[] | Promise { 36 | const res: ExplorerNode[] = []; 37 | if (this.symbolInfo?.children?.length) { 38 | this.symbolInfo.children.forEach((child) => { 39 | res.push(new DocumentSymbolNode(child, this.getParent() as PrimaryTypeNode)); 40 | }); 41 | } 42 | return res; 43 | } 44 | 45 | public getTreeItem(): TreeItem | Promise { 46 | const item = new TreeItem(this.getDisplayName(), 47 | this.symbolInfo?.children?.length ? TreeItemCollapsibleState.Collapsed 48 | : TreeItemCollapsibleState.None); 49 | item.iconPath = this.iconPath; 50 | item.command = this.command; 51 | return item; 52 | } 53 | 54 | public get range(): Range { 55 | // Using `selectionRange` instead of `range` to make sure the cursor will be pointing to the codes, not the comments 56 | return this.symbolInfo.selectionRange; 57 | } 58 | 59 | public computeContextValue(): string | undefined { 60 | return `java:${Explorer.ContextValueType.Symbol}`; 61 | } 62 | 63 | protected get iconPath(): ThemeIcon { 64 | if (this._iconMap.has(this.symbolInfo.kind)) { 65 | const symbolKind = this._iconMap.get(this.symbolInfo.kind); 66 | return new ThemeIcon(`symbol-${symbolKind}`); 67 | } 68 | return new ThemeIcon("symbol-misc"); 69 | } 70 | 71 | protected get command(): Command { 72 | return { 73 | title: "Go to outline", 74 | command: Commands.VIEW_PACKAGE_OUTLINE, 75 | arguments: [(this.getParent() as PrimaryTypeNode).uri, this.range], 76 | }; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/views/explorerNode.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { Command, ProviderResult, TreeItem } from "vscode"; 5 | 6 | export abstract class ExplorerNode { 7 | 8 | constructor(private _parent?: ExplorerNode) { 9 | } 10 | 11 | public getParent(): ExplorerNode | undefined { 12 | return this._parent; 13 | } 14 | 15 | public isItselfOrAncestorOf(node: ExplorerNode | undefined | null, levelToCheck: number = Number.MAX_VALUE) { 16 | while (node && levelToCheck >= 0) { 17 | if (this === node) { 18 | return true; 19 | } 20 | node = node.getParent(); 21 | levelToCheck--; 22 | } 23 | 24 | return false; 25 | } 26 | 27 | protected get command(): Command | undefined { 28 | return undefined; 29 | } 30 | 31 | public abstract getChildren(): ProviderResult; 32 | 33 | public abstract getTreeItem(): TreeItem | Promise; 34 | 35 | public abstract computeContextValue(): string | undefined; 36 | 37 | public abstract getDisplayName(): string; 38 | } 39 | -------------------------------------------------------------------------------- /src/views/fileNode.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { Command, ThemeIcon, Uri } from "vscode"; 5 | import { Commands } from "../commands"; 6 | import { Explorer } from "../constants"; 7 | import { INodeData } from "../java/nodeData"; 8 | import { DataNode } from "./dataNode"; 9 | import { ExplorerNode } from "./explorerNode"; 10 | 11 | export class FileNode extends DataNode { 12 | constructor(nodeData: INodeData, parent: DataNode) { 13 | super(nodeData, parent); 14 | } 15 | 16 | protected hasChildren(): boolean { 17 | return false; 18 | } 19 | 20 | protected async loadData(): Promise { 21 | return undefined; 22 | } 23 | 24 | protected createChildNodeList(): ExplorerNode[] | undefined { 25 | return undefined; 26 | } 27 | 28 | protected get iconPath(): ThemeIcon { 29 | return ThemeIcon.File; 30 | } 31 | 32 | protected get command(): Command { 33 | return { 34 | title: "Open file", 35 | command: Commands.VSCODE_OPEN, 36 | arguments: [Uri.parse(this.uri || ""), { preserveFocus: true }], 37 | }; 38 | } 39 | 40 | protected get contextValue(): string { 41 | return Explorer.ContextValueType.File; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/views/folderNode.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { ThemeIcon } from "vscode"; 5 | import { Explorer } from "../constants"; 6 | import { Jdtls } from "../java/jdtls"; 7 | import { INodeData, NodeKind } from "../java/nodeData"; 8 | import { DataNode } from "./dataNode"; 9 | import { ExplorerNode } from "./explorerNode"; 10 | import { ProjectNode } from "./projectNode"; 11 | import { NodeFactory } from "./nodeFactory"; 12 | 13 | export class FolderNode extends DataNode { 14 | constructor(nodeData: INodeData, parent: DataNode, private _project: ProjectNode, private _rootNode?: DataNode) { 15 | super(nodeData, parent); 16 | } 17 | 18 | protected async loadData(): Promise { 19 | return Jdtls.getPackageData({ 20 | kind: NodeKind.Folder, 21 | projectUri: this._project.uri, 22 | path: this.path, 23 | rootPath: this._rootNode?.path, 24 | handlerIdentifier: this._rootNode?.handlerIdentifier, 25 | }); 26 | } 27 | 28 | protected createChildNodeList(): ExplorerNode[] { 29 | const result: (ExplorerNode | undefined)[] = []; 30 | if (this.nodeData.children && this.nodeData.children.length) { 31 | this.nodeData.children.forEach((nodeData) => { 32 | result.push(NodeFactory.createNode(nodeData, this, this._project, this._rootNode)); 33 | }); 34 | } 35 | return result.filter((n?: T): n is T => Boolean(n)); 36 | } 37 | 38 | protected get iconPath(): ThemeIcon { 39 | return new ThemeIcon("folder"); 40 | } 41 | 42 | protected get contextValue(): string { 43 | return Explorer.ContextValueType.Folder; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/views/hierarchicalPackageNode.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as _ from "lodash"; 5 | import { TreeItem, TreeItemCollapsibleState } from "vscode"; 6 | import { HierarchicalPackageNodeData } from "../java/hierarchicalPackageNodeData"; 7 | import { INodeData } from "../java/nodeData"; 8 | import { explorerLock } from "../utils/Lock"; 9 | import { DataNode } from "./dataNode"; 10 | import { ExplorerNode } from "./explorerNode"; 11 | import { PackageNode } from "./packageNode"; 12 | import { ProjectNode } from "./projectNode"; 13 | import { NodeFactory } from "./nodeFactory"; 14 | 15 | export class HierarchicalPackageNode extends PackageNode { 16 | 17 | constructor(nodeData: INodeData, parent: DataNode, protected _project: ProjectNode, protected _rootNode: DataNode) { 18 | super(nodeData, parent, _project, _rootNode); 19 | } 20 | 21 | public getTreeItem(): TreeItem | Promise { 22 | const item = new TreeItem(this.getHierarchicalNodeData().displayName, 23 | this.hasChildren() ? TreeItemCollapsibleState.Collapsed : TreeItemCollapsibleState.None); 24 | return { ...super.getTreeItem(), ...item }; 25 | } 26 | 27 | public async getChildren(): Promise { 28 | try { 29 | await explorerLock.acquireAsync(); 30 | const data = await this.loadData(); 31 | if (data) { 32 | if (this.nodeData?.children) { 33 | this.nodeData.children.push(...data); 34 | this.nodeData.children = _.uniqBy(this.nodeData.children, (child: INodeData) => [child.path, child.name].join()); 35 | } else { 36 | this.nodeData.children = data; 37 | } 38 | } 39 | this._childrenNodes = this.createChildNodeList() || []; 40 | this.sort(); 41 | return this._childrenNodes; 42 | } finally { 43 | explorerLock.release(); 44 | } 45 | } 46 | 47 | public async revealPaths(paths: INodeData[]): Promise { 48 | const hierarchicalNodeData = paths[0]; 49 | if (hierarchicalNodeData.name === this.nodeData.name) { 50 | paths.shift(); 51 | // reveal as a package node 52 | return super.revealPaths(paths); 53 | } else { 54 | const children: ExplorerNode[] = await this.getChildren(); 55 | const childNode = children.find((child: DataNode) => 56 | hierarchicalNodeData.name.startsWith(child.nodeData.name + ".") || hierarchicalNodeData.name === child.nodeData.name); 57 | return childNode ? childNode.revealPaths(paths) : undefined; 58 | } 59 | } 60 | 61 | protected async loadData(): Promise { 62 | // Load data only when current node is a package 63 | return this.getHierarchicalNodeData().isPackage ? super.loadData() : []; 64 | } 65 | 66 | protected createChildNodeList(): ExplorerNode[] { 67 | const result: (ExplorerNode | undefined)[] = []; 68 | if (this.nodeData.children && this.nodeData.children.length) { 69 | this.nodeData.children.forEach((nodeData) => { 70 | result.push(NodeFactory.createNode(nodeData, this, this._project, this._rootNode)); 71 | }); 72 | } 73 | return result.filter((n?: T): n is T => Boolean(n)); 74 | } 75 | 76 | private getHierarchicalNodeData(): HierarchicalPackageNodeData { 77 | return this.nodeData; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/views/hierarchicalPackageRootNode.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { HierarchicalPackageNodeData } from "../java/hierarchicalPackageNodeData"; 5 | import { INodeData, NodeKind } from "../java/nodeData"; 6 | import { DataNode } from "./dataNode"; 7 | import { ExplorerNode } from "./explorerNode"; 8 | import { HierarchicalPackageNode } from "./hierarchicalPackageNode"; 9 | import { NodeFactory } from "./nodeFactory"; 10 | import { PackageRootNode } from "./packageRootNode"; 11 | import { ProjectNode } from "./projectNode"; 12 | 13 | export class HierarchicalPackageRootNode extends PackageRootNode { 14 | 15 | constructor(nodeData: INodeData, parent: DataNode, _project: ProjectNode) { 16 | super(nodeData, parent, _project); 17 | } 18 | 19 | public async revealPaths(paths: INodeData[]): Promise { 20 | const hierarchicalNodeData = paths[0]; 21 | const children: ExplorerNode[] = await this.getChildren(); 22 | const childNode = children.find((child: DataNode) => 23 | hierarchicalNodeData.name.startsWith(child.nodeData.name + ".") || hierarchicalNodeData.name === child.nodeData.name); 24 | // don't shift when child node is an hierarchical node, or it may lose data of package node 25 | if (!(childNode instanceof HierarchicalPackageNode)) { 26 | paths.shift(); 27 | } 28 | return (childNode && paths.length > 0) ? childNode.revealPaths(paths) : childNode; 29 | } 30 | 31 | protected createChildNodeList(): ExplorerNode[] { 32 | const result: (ExplorerNode | undefined)[] = []; 33 | const packageData: any[] = []; 34 | if (this.nodeData.children && this.nodeData.children.length) { 35 | this.nodeData.children.forEach((nodeData) => { 36 | if (nodeData.kind === NodeKind.Package) { 37 | // Invisible project may have an empty named package root (the linked folder), 38 | // in that case, we will skip it. 39 | packageData.push(nodeData); 40 | } else { 41 | result.push(NodeFactory.createNode(nodeData, this, this._project, this)); 42 | } 43 | }); 44 | } 45 | 46 | if (packageData.length > 0) { 47 | const data: HierarchicalPackageNodeData = HierarchicalPackageNodeData.createHierarchicalNodeDataByPackageList(packageData); 48 | if (data) { 49 | result.push(...data.children.map(d => NodeFactory.createNode(d, this, this._project, this))); 50 | } 51 | } 52 | 53 | return result.filter((n?: T): n is T => Boolean(n)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/views/nodeCache/Trie.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as path from "path"; 5 | import { Uri } from "vscode"; 6 | 7 | export class Trie { 8 | private _root: TrieNode; 9 | 10 | constructor() { 11 | this._root = new TrieNode(undefined); 12 | } 13 | 14 | public insert(input: T): void { 15 | if (!input.uri) { 16 | return; 17 | } 18 | let currentNode: TrieNode = this.root; 19 | const fsPath: string = Uri.parse(input.uri).fsPath; 20 | const segments: string[] = fsPath.split(path.sep); 21 | 22 | for (const segment of segments) { 23 | if (!segment) { 24 | continue; 25 | } 26 | if (!currentNode.children[segment]) { 27 | currentNode.children[segment] = new TrieNode(undefined); 28 | } 29 | currentNode = currentNode.children[segment]; 30 | } 31 | 32 | currentNode.value = input; 33 | } 34 | 35 | public find(fsPath: string, returnEarly: boolean = false): TrieNode | undefined { 36 | let currentNode = this.root; 37 | const segments: string[] = fsPath.split(path.sep); 38 | 39 | for (const segment of segments) { 40 | if (!segment) { 41 | continue; 42 | } 43 | if (returnEarly && currentNode.value) { 44 | return currentNode; 45 | } 46 | if (currentNode.children[segment]) { 47 | currentNode = currentNode.children[segment]; 48 | } else { 49 | return undefined; 50 | } 51 | } 52 | 53 | return currentNode; 54 | } 55 | 56 | public findFirstAncestorNodeWithData(fsPath: string): TrieNode | undefined { 57 | let currentNode: TrieNode = this.root; 58 | let res: TrieNode | undefined; 59 | const segments: string[] = fsPath.split(path.sep); 60 | 61 | for (const segment of segments) { 62 | if (!segment) { 63 | continue; 64 | } 65 | if (currentNode.children[segment]) { 66 | currentNode = currentNode.children[segment]; 67 | } else { 68 | break; 69 | } 70 | 71 | if (currentNode.value) { 72 | res = currentNode; 73 | } 74 | } 75 | 76 | return res; 77 | } 78 | 79 | public get root(): TrieNode { 80 | return this._root; 81 | } 82 | } 83 | 84 | export interface IUriData { 85 | uri?: string; 86 | } 87 | 88 | export class TrieNode { 89 | private _value?: T; 90 | private _children: INodeChildren; 91 | 92 | constructor(value: T) { 93 | this._value = value; 94 | this._children = {}; 95 | } 96 | 97 | public get children(): INodeChildren { 98 | return this._children; 99 | } 100 | 101 | public set children(children: INodeChildren) { 102 | this._children = children; 103 | } 104 | 105 | public set value(value: T | undefined) { 106 | this._value = value; 107 | } 108 | 109 | public get value(): T | undefined { 110 | return this._value; 111 | } 112 | 113 | } 114 | 115 | interface INodeChildren { 116 | [key: string]: TrieNode; 117 | } 118 | -------------------------------------------------------------------------------- /src/views/nodeCache/explorerNodeCache.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as path from "path"; 5 | import { Uri } from "vscode"; 6 | import { DataNode } from "../dataNode"; 7 | import { ExplorerNode } from "../explorerNode"; 8 | import { Trie, TrieNode } from "./Trie"; 9 | 10 | class ExplorerNodeCache { 11 | 12 | private mutableNodeCache: Trie = new Trie(); 13 | 14 | public getDataNode(uri: Uri): DataNode | undefined { 15 | return this.mutableNodeCache.find(uri.fsPath)?.value; 16 | } 17 | 18 | /** 19 | * Find the node whose uri is best match to the input uri, which means 20 | * the uri of the returned node is equal to or ancestor of the input one. 21 | * @param uri 22 | */ 23 | public findBestMatchNodeByUri(uri: Uri): DataNode | undefined { 24 | const parentDir = path.dirname(uri.fsPath); 25 | const ancestor = this.mutableNodeCache.findFirstAncestorNodeWithData(parentDir); 26 | return ancestor?.value; 27 | } 28 | 29 | public saveNode(node: ExplorerNode): void { 30 | if (node instanceof DataNode && node.uri) { 31 | this.mutableNodeCache.insert(node); 32 | } 33 | } 34 | 35 | public saveNodes(nodes: ExplorerNode[]): void { 36 | for (const node of nodes) { 37 | this.saveNode(node); 38 | } 39 | } 40 | 41 | public removeNodeChildren(node?: ExplorerNode): void { 42 | if (!node) { 43 | this.removeChildren(this.mutableNodeCache.root); 44 | return; 45 | } 46 | 47 | if (!(node instanceof DataNode) || !node.uri) { 48 | return; 49 | } 50 | 51 | const trieNode = this.mutableNodeCache.find(Uri.parse(node.uri).fsPath); 52 | if (trieNode) { 53 | this.removeChildren(trieNode); 54 | } 55 | } 56 | 57 | private removeChildren(trieNode: TrieNode) { 58 | trieNode.children = {}; 59 | if (trieNode.value?.nodeData?.children) { 60 | trieNode.value.nodeData.children = undefined; 61 | } 62 | } 63 | } 64 | 65 | export const explorerNodeCache: ExplorerNodeCache = new ExplorerNodeCache(); 66 | -------------------------------------------------------------------------------- /src/views/nodeFactory.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { sendError } from "vscode-extension-telemetry-wrapper"; 5 | import { INodeData, NodeKind } from "../java/nodeData"; 6 | import { Settings } from "../settings"; 7 | import { PrimaryTypeNode } from "./PrimaryTypeNode"; 8 | import { ContainerNode } from "./containerNode"; 9 | import { DataNode } from "./dataNode"; 10 | import { ExplorerNode } from "./explorerNode"; 11 | import { FileNode } from "./fileNode"; 12 | import { FolderNode } from "./folderNode"; 13 | import { HierarchicalPackageNode } from "./hierarchicalPackageNode"; 14 | import { HierarchicalPackageRootNode } from "./hierarchicalPackageRootNode"; 15 | import { PackageNode } from "./packageNode"; 16 | import { PackageRootNode } from "./packageRootNode"; 17 | import { ProjectNode } from "./projectNode"; 18 | import { WorkspaceNode } from "./workspaceNode"; 19 | import { HierarchicalPackageNodeData } from "../java/hierarchicalPackageNodeData"; 20 | 21 | export class NodeFactory { 22 | /** 23 | * Factory method to create explorer node. 24 | * @param nodeData INodeData instance. 25 | * @param parent parent of this node. 26 | * @param project project node that this node belongs to. 27 | * @param rootNode package root node that this node belongs to. 28 | */ 29 | public static createNode(nodeData: INodeData, parent?: DataNode, project?: ProjectNode, rootNode?: DataNode): ExplorerNode | undefined { 30 | const isHierarchicalView = Settings.isHierarchicalView(); 31 | try { 32 | switch (nodeData.kind) { 33 | case NodeKind.Workspace: 34 | return new WorkspaceNode(nodeData, parent); 35 | case NodeKind.Project: 36 | return new ProjectNode(nodeData, parent); 37 | case NodeKind.Container: 38 | if (!parent || !project) { 39 | throw new Error("Container node must have parent and project."); 40 | } 41 | 42 | return new ContainerNode(nodeData, parent, project); 43 | case NodeKind.PackageRoot: 44 | if (!parent || !project) { 45 | throw new Error("Package root node must have parent and project."); 46 | } 47 | 48 | if (isHierarchicalView) { 49 | return new HierarchicalPackageRootNode(nodeData, parent, project); 50 | } 51 | return new PackageRootNode(nodeData, parent, project); 52 | case NodeKind.Package: 53 | if (!parent || !project || !rootNode) { 54 | throw new Error("Package node must have parent, project and root."); 55 | } 56 | 57 | if (nodeData instanceof HierarchicalPackageNodeData) { 58 | return new HierarchicalPackageNode(nodeData, parent, project, rootNode); 59 | } 60 | return new PackageNode(nodeData, parent, project, rootNode); 61 | case NodeKind.PrimaryType: 62 | if (nodeData.metaData && nodeData.metaData[PrimaryTypeNode.K_TYPE_KIND]) { 63 | if (!parent) { 64 | throw new Error("Primary type node must have parent."); 65 | } 66 | 67 | return new PrimaryTypeNode(nodeData, parent, rootNode); 68 | } 69 | return undefined; 70 | case NodeKind.Folder: 71 | if (!parent || !project) { 72 | throw new Error("Folder node must have parent and project."); 73 | } 74 | 75 | return new FolderNode(nodeData, parent, project, rootNode); 76 | case NodeKind.CompilationUnit: 77 | case NodeKind.ClassFile: 78 | case NodeKind.File: 79 | if (!parent) { 80 | throw new Error("Folder node must have parent."); 81 | } 82 | 83 | return new FileNode(nodeData, parent); 84 | default: 85 | throw new Error(`Unsupported node kind: ${nodeData.kind}`); 86 | } 87 | } catch (error) { 88 | sendError(new Error(`Unsupported node kind: ${nodeData.kind}`)); 89 | return undefined; 90 | } 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/views/packageNode.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { ThemeIcon } from "vscode"; 5 | import { Explorer } from "../constants"; 6 | import { Jdtls } from "../java/jdtls"; 7 | import { INodeData, NodeKind } from "../java/nodeData"; 8 | import { IPackageRootNodeData, PackageRootKind } from "../java/packageRootNodeData"; 9 | import { isTest } from "../utility"; 10 | import { DataNode } from "./dataNode"; 11 | import { ExplorerNode } from "./explorerNode"; 12 | import { ProjectNode } from "./projectNode"; 13 | import { NodeFactory } from "./nodeFactory"; 14 | 15 | export class PackageNode extends DataNode { 16 | constructor(nodeData: INodeData, parent: DataNode, protected _project: ProjectNode, protected _rootNode: DataNode) { 17 | super(nodeData, parent); 18 | } 19 | 20 | public isSourcePackage(): boolean { 21 | const parentData = this._rootNode.nodeData; 22 | return parentData.entryKind === PackageRootKind.K_SOURCE || parentData.kind === NodeKind.Project; 23 | } 24 | 25 | protected async loadData(): Promise { 26 | return Jdtls.getPackageData({ 27 | kind: NodeKind.Package, 28 | projectUri: this._project.nodeData.uri, 29 | path: this.nodeData.name, 30 | handlerIdentifier: this.nodeData.handlerIdentifier, 31 | }); 32 | } 33 | 34 | protected createChildNodeList(): ExplorerNode[] { 35 | const result: (ExplorerNode | undefined)[] = []; 36 | if (this.nodeData.children && this.nodeData.children.length) { 37 | this.nodeData.children.forEach((nodeData) => { 38 | result.push(NodeFactory.createNode(nodeData, this, this._project, this._rootNode)); 39 | }); 40 | } 41 | return result.filter((n?: T): n is T => Boolean(n)); 42 | } 43 | 44 | protected get iconPath(): ThemeIcon { 45 | return new ThemeIcon("symbol-package"); 46 | } 47 | 48 | protected get contextValue(): string | undefined { 49 | const parentData = this._rootNode.nodeData; 50 | let contextValue: string = Explorer.ContextValueType.Package; 51 | if (parentData.entryKind === PackageRootKind.K_SOURCE || parentData.kind === NodeKind.Project) { 52 | contextValue += "+source"; 53 | if (this._project.nodeData.metaData?.MaxSourceVersion >= 16) { 54 | contextValue += "+allowRecord"; 55 | } 56 | } else if (parentData.entryKind === PackageRootKind.K_BINARY) { 57 | contextValue += "+binary"; 58 | } 59 | if (isTest(parentData)) { 60 | contextValue += "+test"; 61 | } 62 | return contextValue; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/views/packageRootNode.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { ThemeIcon } from "vscode"; 5 | import { Explorer } from "../constants"; 6 | import { Jdtls } from "../java/jdtls"; 7 | import { INodeData, NodeKind } from "../java/nodeData"; 8 | import { IPackageRootNodeData, PackageRootKind } from "../java/packageRootNodeData"; 9 | import { Settings } from "../settings"; 10 | import { isTest } from "../utility"; 11 | import { ContainerNode } from "./containerNode"; 12 | import { DataNode } from "./dataNode"; 13 | import { ExplorerNode } from "./explorerNode"; 14 | import { ProjectNode } from "./projectNode"; 15 | import { NodeFactory } from "./nodeFactory"; 16 | 17 | export class PackageRootNode extends DataNode { 18 | 19 | constructor(nodeData: INodeData, parent: DataNode, protected _project: ProjectNode) { 20 | super(nodeData, parent); 21 | } 22 | 23 | public isSourceRoot(): boolean { 24 | return (this.nodeData).entryKind === PackageRootKind.K_SOURCE; 25 | } 26 | 27 | protected async loadData(): Promise { 28 | return Jdtls.getPackageData({ 29 | kind: NodeKind.PackageRoot, 30 | projectUri: this._project.nodeData.uri, 31 | rootPath: this.nodeData.path, 32 | handlerIdentifier: this.nodeData.handlerIdentifier, 33 | isHierarchicalView: Settings.isHierarchicalView(), 34 | }); 35 | } 36 | 37 | protected createChildNodeList(): ExplorerNode[] { 38 | const result: (ExplorerNode | undefined)[] = []; 39 | if (this.nodeData.children && this.nodeData.children.length) { 40 | this.nodeData.children.forEach((nodeData) => { 41 | result.push(NodeFactory.createNode(nodeData, this, this._project, this)); 42 | }); 43 | } 44 | return result.filter((n?: T): n is T => Boolean(n)); 45 | } 46 | 47 | protected get description(): string | boolean | undefined { 48 | const data = this.nodeData; 49 | if (data.entryKind === PackageRootKind.K_BINARY) { 50 | return data.path; 51 | } else { 52 | return undefined; 53 | } 54 | } 55 | 56 | protected get contextValue(): string { 57 | const data = this.nodeData; 58 | let contextValue: string; 59 | if (data.entryKind === PackageRootKind.K_BINARY) { 60 | contextValue = Explorer.ContextValueType.Jar; 61 | const parent = this.getParent(); 62 | // currently, we only support editing referenced libraries in unmanaged folders 63 | if (parent.path?.startsWith("REFERENCED_LIBRARIES_PATH") && this._project.isUnmanagedFolder()) { 64 | contextValue += "+referencedLibrary"; 65 | } 66 | return contextValue; 67 | } else { 68 | contextValue = Explorer.ContextValueType.PackageRoot; 69 | if (isTest(data)) { 70 | contextValue += "+test"; 71 | } 72 | if (resourceRoots.includes(this._nodeData.name)) { 73 | // APIs in JDT does not have a consistent result telling whether a package root 74 | // is a source root or resource root, so we hard code some common resources root 75 | // here as a workaround. 76 | contextValue += "+resource"; 77 | } else { 78 | contextValue += "+source"; 79 | } 80 | if (this._project.nodeData.metaData?.MaxSourceVersion >= 16) { 81 | contextValue += "+allowRecord"; 82 | } 83 | return contextValue; 84 | } 85 | } 86 | 87 | protected get iconPath(): ThemeIcon { 88 | const data = this.nodeData; 89 | if (data.moduleName || data.entryKind === PackageRootKind.K_SOURCE) { 90 | return new ThemeIcon("file-submodule"); 91 | } 92 | // K_BINARY node 93 | return new ThemeIcon("file-zip"); 94 | } 95 | } 96 | 97 | export const resourceRoots: string[] = ["src/main/resources", "src/test/resources"]; 98 | -------------------------------------------------------------------------------- /src/views/workspaceNode.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import { ThemeIcon } from "vscode"; 5 | import { Explorer } from "../constants"; 6 | import { Jdtls } from "../java/jdtls"; 7 | import { INodeData } from "../java/nodeData"; 8 | import { DataNode } from "./dataNode"; 9 | import { ExplorerNode } from "./explorerNode"; 10 | import { NodeFactory } from "./nodeFactory"; 11 | 12 | export class WorkspaceNode extends DataNode { 13 | constructor(nodeData: INodeData, parent?: DataNode) { 14 | super(nodeData, parent); 15 | } 16 | 17 | protected async loadData(): Promise { 18 | if (!this.nodeData.uri) { 19 | return undefined; 20 | } 21 | return Jdtls.getProjects(this.nodeData.uri); 22 | } 23 | 24 | protected createChildNodeList(): ExplorerNode[] { 25 | const result: (ExplorerNode | undefined)[] = []; 26 | if (this.nodeData.children && this.nodeData.children.length) { 27 | this.nodeData.children.forEach((nodeData) => { 28 | result.push(NodeFactory.createNode(nodeData, this)); 29 | }); 30 | } 31 | return result.filter((n?: T): n is T => Boolean(n)); 32 | } 33 | 34 | protected get iconPath(): ThemeIcon { 35 | return new ThemeIcon("root-folder"); 36 | } 37 | 38 | protected get contextValue(): string { 39 | return Explorer.ContextValueType.WorkspaceFolder; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /templates/invisible-project/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.project.sourcePaths": ["src"], 3 | "java.project.outputPath": "bin", 4 | "java.project.referencedLibraries": [ 5 | "lib/**/*.jar" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /templates/invisible-project/README.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | Welcome to the VS Code Java world. Here is a guideline to help you get started to write Java code in Visual Studio Code. 4 | 5 | ## Folder Structure 6 | 7 | The workspace contains two folders by default, where: 8 | 9 | - `src`: the folder to maintain sources 10 | - `lib`: the folder to maintain dependencies 11 | 12 | Meanwhile, the compiled output files will be generated in the `bin` folder by default. 13 | 14 | > If you want to customize the folder structure, open `.vscode/settings.json` and update the related settings there. 15 | 16 | ## Dependency Management 17 | 18 | The `JAVA PROJECTS` view allows you to manage your dependencies. More details can be found [here](https://github.com/microsoft/vscode-java-dependency#manage-dependencies). 19 | -------------------------------------------------------------------------------- /templates/invisible-project/src/App.java: -------------------------------------------------------------------------------- 1 | public class App { 2 | public static void main(String[] args) throws Exception { 3 | System.out.println("Hello, World!"); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/gradle-suite/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as glob from "glob"; 5 | import * as Mocha from "mocha"; 6 | import * as path from "path"; 7 | 8 | export function run(): Promise { 9 | // Create the mocha test 10 | const mocha = new Mocha({ 11 | ui: "tdd", 12 | color: true, 13 | timeout: 1 * 60 * 1000, 14 | }); 15 | 16 | const testsRoot = __dirname; 17 | 18 | return new Promise((c, e) => { 19 | glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { 20 | if (err) { 21 | return e(err); 22 | } 23 | 24 | // Add files to the test suite 25 | files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); 26 | 27 | try { 28 | // Run the mocha test 29 | mocha.run((failures) => { 30 | if (failures > 0) { 31 | e(new Error(`${failures} tests failed.`)); 32 | } else { 33 | c(); 34 | } 35 | }); 36 | } catch (err) { 37 | e(err); 38 | } 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /test/gradle-suite/projectView.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as assert from "assert"; 5 | import { ContainerNode, contextManager, DataNode, DependencyExplorer, 6 | languageServerApiManager, 7 | PackageRootNode, PrimaryTypeNode, ProjectNode } from "../../extension.bundle"; 8 | import { fsPath, setupTestEnv, Uris } from "../shared"; 9 | 10 | // tslint:disable: only-arrow-functions 11 | suite("Gradle Project View Tests", () => { 12 | 13 | suiteSetup(async () => { 14 | await setupTestEnv(); 15 | await languageServerApiManager.ready(); 16 | }); 17 | 18 | test("Can node render correctly", async function() { 19 | const explorer = DependencyExplorer.getInstance(contextManager.context); 20 | 21 | // validate root nodes 22 | const roots = await explorer.dataProvider.getChildren(); 23 | assert.equal(roots?.length, 1, "Number of root node should be 1"); 24 | const projectNode = roots![0] as ProjectNode; 25 | assert.equal(projectNode.name, "gradle", "Project name should be \"gradle\""); 26 | 27 | // validate package root/dependency nodes 28 | const projectChildren = await projectNode.getChildren(); 29 | assert.ok(!!projectChildren.find((c: DataNode) => c.name === "build.gradle")); 30 | assert.ok(!!projectChildren.find((c: DataNode) => c.name === ".vscode")); 31 | const mainPackage = projectChildren[0] as PackageRootNode; 32 | assert.equal(mainPackage.name, "src/main/java", "Package name should be \"src/main/java\""); 33 | const systemLibrary = projectChildren[1] as ContainerNode; 34 | const gradleDependency = projectChildren[2] as ContainerNode; 35 | // only match prefix of system library since JDK version may differ 36 | assert.ok(systemLibrary.name.startsWith("JRE System Library"), "Container name should start with JRE System Library"); 37 | assert.equal(gradleDependency.name, "Project and External Dependencies", "Container name should be \"Project and External Dependencies\""); 38 | 39 | // validate innermost layer nodes 40 | const mainClasses = await mainPackage.getChildren(); 41 | assert.equal(mainClasses.length, 1, "Number of main classes should be 1"); 42 | const mainClass = mainClasses[0] as PrimaryTypeNode; 43 | assert.equal(mainClass.name, "GradleTest", "Name of main class should be \"GradleTest\""); 44 | }); 45 | 46 | test("Can node have correct uri", async function() { 47 | const explorer = DependencyExplorer.getInstance(contextManager.context); 48 | 49 | const projectNode = (await explorer.dataProvider.getChildren())![0] as ProjectNode; 50 | const mainPackage = (await projectNode.getChildren())[0] as PackageRootNode; 51 | const mainClass = (await mainPackage.getChildren())[0] as PrimaryTypeNode; 52 | 53 | assert.equal(fsPath(projectNode), Uris.GRADLE_PROJECT_NODE, "Project uri incorrect"); 54 | assert.equal(fsPath(mainPackage), Uris.GRADLE_MAIN_PACKAGE, "Main root package uri incorrect"); 55 | assert.equal(fsPath(mainClass), Uris.GRADLE_MAIN_CLASS, "Main class uri incorrect"); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/gradle/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "java", 6 | "name": "Java Debug", 7 | "request": "launch", 8 | "cwd": "${workspaceRoot}", 9 | "stopOnEntry": true, 10 | "mainClass": "GradleTest", 11 | "sourcePaths": [ 12 | "${workspaceRoot}/src/main/java" 13 | ], 14 | "options": [] 15 | }, 16 | { 17 | "type": "java", 18 | "name": "Java Debug (Attach)", 19 | "request": "attach", 20 | "hostName": "localhost", 21 | "port": 0, 22 | "attachTimeout": 30000, 23 | "cwd": "${workspaceRoot}", 24 | "sourcePaths": [ 25 | "${workspaceRoot}" 26 | ] 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /test/gradle/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.server.launchMode": "Standard", 3 | "java.import.gradle.version": "7.3" 4 | } 5 | -------------------------------------------------------------------------------- /test/gradle/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | 4 | sourceSets { 5 | main.java.srcDirs = ['src/main/java'] 6 | } 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | implementation 'com.google.code.gson:gson:2.8.0' 14 | } 15 | -------------------------------------------------------------------------------- /test/gradle/src/main/java/GradleTest.java: -------------------------------------------------------------------------------- 1 | public class GradleTest { 2 | public static void main(String[] args) { 3 | System.out.println("This is a sample gradle project"); 4 | } 5 | } -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as cp from "child_process"; 5 | import * as fs from "fs"; 6 | import * as os from "os"; 7 | import * as path from "path"; 8 | import { downloadAndUnzipVSCode, resolveCliArgsFromVSCodeExecutablePath, runTests } from "@vscode/test-electron"; 9 | 10 | async function main(): Promise { 11 | try { 12 | // test fails in macOS since the limitation of path length 13 | // See: https://github.com/microsoft/vscode/issues/86382#issuecomment-593765388 14 | const userDir = fs.mkdtempSync(path.join(os.tmpdir(), "vscode-user")); 15 | const vscodeExecutablePath = await downloadAndUnzipVSCode(); 16 | 17 | // Resolve redhat.java dependency 18 | const [cli, ...args] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath); 19 | const options: cp.SpawnSyncOptionsWithStringEncoding = { 20 | encoding: 'utf-8', 21 | stdio: 'inherit', 22 | }; 23 | if (process.platform === 'win32') { 24 | options.shell = true; 25 | } 26 | cp.spawnSync(cli, [...args, '--install-extension', 'redhat.java'], options); 27 | 28 | // The folder containing the Extension Manifest package.json 29 | // Passed to `--extensionDevelopmentPath` 30 | const extensionDevelopmentPath: string = path.resolve(__dirname, "../../"); 31 | 32 | // Download VS Code, unzip it and run the integration test 33 | 34 | // Run general test 35 | await runTests({ 36 | vscodeExecutablePath, 37 | extensionDevelopmentPath, 38 | extensionTestsPath: path.resolve(__dirname, "./suite"), 39 | launchArgs: [ 40 | path.join(__dirname, "..", "..", "test", "java9"), 41 | `--user-data-dir=${userDir}`, 42 | ], 43 | }); 44 | 45 | // Run test for simple project 46 | await runTests({ 47 | vscodeExecutablePath, 48 | extensionDevelopmentPath, 49 | extensionTestsPath: path.resolve(__dirname, "./simple-suite"), 50 | launchArgs: [ 51 | path.join(__dirname, "..", "..", "test", "simple"), 52 | `--user-data-dir=${userDir}`, 53 | ], 54 | }); 55 | 56 | // Run test for maven project 57 | await runTests({ 58 | vscodeExecutablePath, 59 | extensionDevelopmentPath, 60 | extensionTestsPath: path.resolve(__dirname, "./maven-suite"), 61 | launchArgs: [ 62 | path.join(__dirname, "..", "..", "test", "maven"), 63 | `--user-data-dir=${userDir}`, 64 | ], 65 | }); 66 | 67 | // Run test for gradle project 68 | await runTests({ 69 | vscodeExecutablePath, 70 | extensionDevelopmentPath, 71 | extensionTestsPath: path.resolve(__dirname, "./gradle-suite"), 72 | launchArgs: [ 73 | path.join(__dirname, "..", "..", "test", "gradle"), 74 | `--user-data-dir=${userDir}`, 75 | ], 76 | }); 77 | 78 | // Run test for invisible project 79 | await runTests({ 80 | vscodeExecutablePath, 81 | extensionDevelopmentPath, 82 | extensionTestsPath: path.resolve(__dirname, "./invisible-suite"), 83 | launchArgs: [ 84 | path.join(__dirname, "..", "..", "test", "invisible"), 85 | `--user-data-dir=${userDir}`, 86 | ], 87 | }); 88 | 89 | 90 | // Run multi module test 91 | await runTests({ 92 | vscodeExecutablePath, 93 | extensionDevelopmentPath, 94 | extensionTestsPath: path.resolve(__dirname, "./multi-module-suite"), 95 | launchArgs: [ 96 | path.join(__dirname, "..", "..", "test", "multi-module"), 97 | `--user-data-dir=${userDir}`, 98 | ], 99 | }); 100 | 101 | process.exit(0); 102 | 103 | } catch (err) { 104 | process.stdout.write(`${err}${os.EOL}`); 105 | process.exit(1); 106 | } 107 | } 108 | 109 | main(); 110 | -------------------------------------------------------------------------------- /test/invisible-suite/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as glob from "glob"; 5 | import * as Mocha from "mocha"; 6 | import * as path from "path"; 7 | 8 | export function run(): Promise { 9 | // Create the mocha test 10 | const mocha = new Mocha({ 11 | ui: "tdd", 12 | color: true, 13 | timeout: 1 * 60 * 1000, 14 | }); 15 | 16 | const testsRoot = __dirname; 17 | 18 | return new Promise((c, e) => { 19 | glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { 20 | if (err) { 21 | return e(err); 22 | } 23 | 24 | // Add files to the test suite 25 | files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); 26 | 27 | try { 28 | // Run the mocha test 29 | mocha.run((failures) => { 30 | if (failures > 0) { 31 | e(new Error(`${failures} tests failed.`)); 32 | } else { 33 | c(); 34 | } 35 | }); 36 | } catch (err) { 37 | e(err); 38 | } 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /test/invisible-suite/projectView.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as assert from "assert"; 5 | import * as fse from "fs-extra"; 6 | import * as path from "path"; 7 | import * as vscode from "vscode"; 8 | import { Commands, contextManager, DependencyExplorer, languageServerApiManager, PackageNode, PackageRootNode, ProjectNode } from "../../extension.bundle"; 9 | import { setupTestEnv } from "../shared"; 10 | import { sleep } from "../util"; 11 | 12 | // tslint:disable: only-arrow-functions 13 | suite("Invisible Project View Tests", () => { 14 | 15 | suiteSetup(async () => { 16 | await setupTestEnv(); 17 | await languageServerApiManager.ready(); 18 | }); 19 | 20 | test("Can execute command java.project.refreshLibraries correctly", async function() { 21 | const explorer = DependencyExplorer.getInstance(contextManager.context); 22 | 23 | let projectNode = (await explorer.dataProvider.getChildren())![0] as ProjectNode; 24 | const projectUri = projectNode.nodeData.uri; 25 | assert.ok(projectUri, "project node doesn't have correct uri"); 26 | const expectedUri = vscode.Uri.parse(projectUri!); 27 | await fse.copy(path.join(expectedUri.fsPath, "libSource", "simple.jar"), path.join(expectedUri.fsPath, "lib", "simple.jar")); 28 | await vscode.commands.executeCommand(Commands.JAVA_PROJECT_REFRESH_LIBRARIES); 29 | await sleep(5000); 30 | projectNode = (await explorer.dataProvider.getChildren())![0] as ProjectNode; 31 | const packageRoots = await projectNode.getChildren(); 32 | assert.equal(packageRoots.length, 3, "length of package nodes should be 3"); 33 | const mainPackage = packageRoots[2] as PackageRootNode; 34 | const libraryNode = (await mainPackage.getChildren())[0] as PackageNode; 35 | assert.equal(libraryNode.nodeData.name, "simple.jar", "library name should be simple.jar"); 36 | }); 37 | 38 | test("Can execute command java.project.removeLibrary correctly", async function() { 39 | const explorer = DependencyExplorer.getInstance(contextManager.context); 40 | 41 | let projectNode = (await explorer.dataProvider.getChildren())![0] as ProjectNode; 42 | let packageRoots = await projectNode.getChildren(); 43 | assert.equal(packageRoots.length, 3, "length of package nodes should be 3"); 44 | let mainPackage = packageRoots[2] as PackageRootNode; 45 | const libraryNode = (await mainPackage.getChildren())[0] as PackageNode; 46 | await vscode.commands.executeCommand(Commands.JAVA_PROJECT_REMOVE_LIBRARY, libraryNode); 47 | await sleep(5000); 48 | projectNode = (await explorer.dataProvider.getChildren())![0] as ProjectNode; 49 | packageRoots = await projectNode.getChildren(); 50 | assert.equal(packageRoots.length, 3, "length of package nodes should be 3"); 51 | mainPackage = packageRoots[2] as PackageRootNode; 52 | assert.equal((await mainPackage.getChildren()).length, 0, "libraries' length should be 0"); 53 | }); 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /test/invisible/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.project.referencedLibraries": [ 3 | "lib/**/*.jar" 4 | ] 5 | } -------------------------------------------------------------------------------- /test/invisible/libSource/simple.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-java-dependency/7b3990cf7d388b0e3fc801734cbff654a5313d17/test/invisible/libSource/simple.jar -------------------------------------------------------------------------------- /test/invisible/src/App.java: -------------------------------------------------------------------------------- 1 | public class App { 2 | public static void main(String[] args) throws Exception { 3 | System.out.println("Hello, World!"); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/java9/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.microsoft.app 5 | java9-app 6 | jar 7 | 1.0-SNAPSHOT 8 | java9-app 9 | http://maven.apache.org 10 | 11 | 12 | junit 13 | junit 14 | 4.13.1 15 | test 16 | 17 | 18 | com.google.code.gson 19 | gson 20 | 2.8.9 21 | 22 | 23 | 24 | 25 | 26 | 27 | org.apache.maven.plugins 28 | maven-compiler-plugin 29 | 3.7.0 30 | 31 | 9 32 | 9 33 | true 34 | true 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /test/java9/src/main/java/com/microsoft/app/App.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.app; 2 | 3 | import com.google.gson.Gson; 4 | 5 | public class App 6 | { 7 | public static void main( String[] args ) 8 | { 9 | Gson GSON = new Gson(); 10 | String name = "jinbwan"; 11 | Employee employee = GSON.fromJson("{\"name\": \"" + name + "\"}", Employee.class); 12 | System.out.println(employee.getName()); 13 | } 14 | 15 | static class Employee { 16 | public String name; 17 | 18 | public String getName() { 19 | return this.name; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/java9/src/main/java/com/microsoft/app/Launcher.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.app; 2 | 3 | import java.nio.file.Paths; 4 | 5 | public class Launcher { 6 | public static void main(String[] args) { 7 | System.out.println(Paths.get(".").toAbsolutePath().toString()); 8 | 9 | System.out.println(System.getenv("Path")); 10 | } 11 | } -------------------------------------------------------------------------------- /test/java9/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module com.microsoft.app.mymodule { 2 | requires gson; 3 | requires java.base; 4 | requires java.sql; 5 | opens com.microsoft.app; 6 | } -------------------------------------------------------------------------------- /test/java9/src/test/java/com/microsoft/app/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.app; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | extends TestCase 12 | { 13 | /** 14 | * Create the test case 15 | * 16 | * @param testName name of the test case 17 | */ 18 | public AppTest( String testName ) 19 | { 20 | super( testName ); 21 | } 22 | 23 | /** 24 | * @return the suite of tests being tested 25 | */ 26 | public static Test suite() 27 | { 28 | return new TestSuite( AppTest.class ); 29 | } 30 | 31 | /** 32 | * Rigourous Test :-) 33 | */ 34 | public void testApp() 35 | { 36 | assertTrue( true ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/maven-suite/buildArtifact.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as assert from "assert"; 5 | import * as cp from "child_process"; 6 | import * as fse from "fs-extra"; 7 | import { suiteTeardown } from "mocha"; 8 | import * as path from "path"; 9 | import { Task, TaskEndEvent, tasks, workspace } from "vscode"; 10 | import { languageServerApiManager } from "../../extension.bundle"; 11 | import { setupTestEnv } from "../shared"; 12 | 13 | // tslint:disable: only-arrow-functions 14 | const folderPath: string = workspace.workspaceFolders![0].uri.fsPath; 15 | const jarFileName: string = "maven.jar"; 16 | const testFolder: string = path.join(folderPath, "test"); 17 | 18 | suite("Build Artifact Tests", () => { 19 | 20 | suiteSetup(setupTestEnv); 21 | 22 | test("Should bypass buildArtifact tasks before language server is ready", async function() { 23 | const vscodeTasks: Task[] = await tasks.fetchTasks(); 24 | const buildJarTask: Task | undefined = vscodeTasks.find((t: Task) => t.name === "java (buildArtifact): maven"); 25 | assert.ok(buildJarTask === undefined); 26 | }); 27 | 28 | test("Can build jar correctly", async function() { 29 | await languageServerApiManager.ready(); 30 | const vscodeTasks: Task[] = await tasks.fetchTasks(); 31 | const buildJarTask: Task | undefined = vscodeTasks.find((t: Task) => t.name === "java (buildArtifact): maven"); 32 | assert.ok(buildJarTask !== undefined); 33 | 34 | await new Promise(async (resolve) => { 35 | tasks.onDidEndTask((e: TaskEndEvent) => { 36 | if (e.execution.task.name === buildJarTask?.name) { 37 | return resolve(); 38 | } 39 | }); 40 | await tasks.executeTask(buildJarTask!); 41 | }); 42 | 43 | const isFileExist: boolean = await fse.pathExists(path.join(folderPath, jarFileName)); 44 | assert.ok(isFileExist); 45 | 46 | await fse.ensureDir(testFolder); 47 | cp.execSync("jar -xvf ../maven.jar", { 48 | cwd: testFolder, 49 | }); 50 | 51 | const isManifestExist: boolean = await fse.pathExists(path.join(testFolder, "META-INF", "MANIFEST.MF")); 52 | assert.ok(isManifestExist); 53 | const isClassFileExist: boolean = await fse.pathExists(path.join(testFolder, "com", "mycompany", "app", "App.class")); 54 | assert.ok(isClassFileExist); 55 | }); 56 | 57 | suiteTeardown(async function() { 58 | await fse.remove(path.join(folderPath, jarFileName)); 59 | await fse.remove(testFolder); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /test/maven-suite/context.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as path from "path"; 5 | import * as assert from "assert"; 6 | import { Diagnostic, DiagnosticSeverity, languages, Position, Range, Uri, window } from "vscode"; 7 | import { contextManager } from "../../extension.bundle"; 8 | import { setupTestEnv, Uris } from "../shared"; 9 | import { sleep } from "../util"; 10 | 11 | // tslint:disable: only-arrow-functions 12 | suite("Context Manager Tests", () => { 13 | 14 | suiteSetup(setupTestEnv); 15 | 16 | test("Can set reload project context correctly", async function() { 17 | assert.strictEqual(!!contextManager.getContextValue("java:reloadProjectActive"), false); 18 | 19 | const pomUri = Uri.file(path.join(Uris.MAVEN_PROJECT_NODE, "pom.xml")); 20 | await window.showTextDocument(pomUri); 21 | assert.strictEqual(!!contextManager.getContextValue("java:reloadProjectActive"), false); 22 | 23 | const collection = languages.createDiagnosticCollection("test-collection"); 24 | collection.set(pomUri, [new Diagnostic( 25 | new Range(new Position(0, 0), new Position(0, 0)), 26 | "The build file has been changed and may need reload to make it effective.", 27 | DiagnosticSeverity.Information 28 | )]); 29 | await sleep(1000); 30 | assert.strictEqual(!!contextManager.getContextValue("java:reloadProjectActive"), true); 31 | 32 | await window.showTextDocument(Uri.file(Uris.MAVEN_MAIN_CLASS)); 33 | assert.strictEqual(!!contextManager.getContextValue("java:reloadProjectActive"), false); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /test/maven-suite/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as glob from "glob"; 5 | import * as Mocha from "mocha"; 6 | import * as path from "path"; 7 | 8 | export function run(): Promise { 9 | // Create the mocha test 10 | const mocha = new Mocha({ 11 | ui: "tdd", 12 | color: true, 13 | timeout: 1 * 60 * 1000, 14 | }); 15 | 16 | const testsRoot = __dirname; 17 | 18 | return new Promise((c, e) => { 19 | glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { 20 | if (err) { 21 | return e(err); 22 | } 23 | 24 | // Add files to the test suite 25 | files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); 26 | 27 | try { 28 | // Run the mocha test 29 | mocha.run((failures) => { 30 | if (failures > 0) { 31 | e(new Error(`${failures} tests failed.`)); 32 | } else { 33 | c(); 34 | } 35 | }); 36 | } catch (err) { 37 | e(err); 38 | } 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /test/maven/.hidden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-java-dependency/7b3990cf7d388b0e3fc801734cbff654a5313d17/test/maven/.hidden -------------------------------------------------------------------------------- /test/maven/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "java", 6 | "name": "Debug (Launch)", 7 | "request": "launch", 8 | "mainClass": "com.mycompany.app.App", 9 | "args": "", 10 | "sourcePaths": [ 11 | "${workspaceRoot}/src/main/java" 12 | ] 13 | }, 14 | { 15 | "type": "java", 16 | "name": "Debug (Attach)", 17 | "request": "attach", 18 | "hostName": "localhost", 19 | "port": 0, 20 | "timeout": 30000, 21 | "sourcePaths": [ 22 | "${workspaceRoot}" 23 | ] 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /test/maven/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.server.launchMode": "Standard", 3 | "java.dependency.packagePresentation": "flat", 4 | "java.dependency.autoRefresh": true, 5 | "java.dependency.syncWithFolderExplorer": true, 6 | "java.project.explorer.showNonJavaResources": true, 7 | // The test code does not expect a .classpath, .settings and .project file to be created. 8 | // If the test is run on a developer machine, the user settings will be inherited and it might be set there. 9 | "java.import.generatesMetadataFilesAtProjectRoot": false, 10 | "files.exclude": { 11 | "**/.hidden": true, 12 | // Overwrite user settings, if they exist 13 | "**/.classpath": false, 14 | "**/.factorypath": false, 15 | "**/.project": false, 16 | "**/.settings": false, 17 | "**/target": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/maven/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "java (buildArtifact)", 6 | "label": "java (buildArtifact): maven", 7 | "mainClass": "com.mycompany.app.App", 8 | "targetPath": "${workspaceFolder}/${workspaceFolderBasename}.jar", 9 | "elements": [ 10 | "${compileOutput}", 11 | "${dependencies}" 12 | ], 13 | "problemMatcher": [] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /test/maven/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.mycompany.app 5 | my-app 6 | jar 7 | 1.0-SNAPSHOT 8 | my-app 9 | http://maven.apache.org 10 | 11 | 17 12 | 17 13 | 14 | 15 | 16 | junit 17 | junit 18 | 4.13.1 19 | test 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/maven/src/main/java/com/mycompany/app/App.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.app; 2 | 3 | /** 4 | * Hello world! 5 | * 6 | */ 7 | public class App 8 | { 9 | public static void main( String[] args ) 10 | { 11 | System.out.println( "Hello World!" ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/maven/src/main/java/com/mycompany/app/AppToDelete.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.app; 2 | 3 | public class AppToDelete { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /test/maven/src/main/java/com/mycompany/app/AppToRename.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.app; 2 | 3 | public class AppToRename { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /test/maven/src/main/java/com/mycompany/app/package-info.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.app; 2 | -------------------------------------------------------------------------------- /test/maven/src/main/java/com/mycompany/app1/App1.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.app1; 2 | 3 | public class App1 { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /test/maven/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module my.app { 2 | exports com.mycompany.app1; 3 | exports com.mycompany.app; 4 | } 5 | -------------------------------------------------------------------------------- /test/maven/src/test/java/com/mycompany/app/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.app; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | extends TestCase 12 | { 13 | /** 14 | * Create the test case 15 | * 16 | * @param testName name of the test case 17 | */ 18 | public AppTest( String testName ) 19 | { 20 | super( testName ); 21 | } 22 | 23 | /** 24 | * @return the suite of tests being tested 25 | */ 26 | public static Test suite() 27 | { 28 | return new TestSuite( AppTest.class ); 29 | } 30 | 31 | /** 32 | * Rigourous Test :-) 33 | */ 34 | public void testApp() 35 | { 36 | assertTrue( true ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/multi-module-suite/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as glob from "glob"; 5 | import * as Mocha from "mocha"; 6 | import * as path from "path"; 7 | 8 | export function run(): Promise { 9 | // Create the mocha test 10 | const mocha = new Mocha({ 11 | ui: "tdd", 12 | color: true, 13 | timeout: 1 * 60 * 1000, 14 | }); 15 | 16 | const testsRoot = __dirname; 17 | 18 | return new Promise((c, e) => { 19 | glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { 20 | if (err) { 21 | return e(err); 22 | } 23 | 24 | // Add files to the test suite 25 | files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); 26 | 27 | try { 28 | // Run the mocha test 29 | mocha.run((failures) => { 30 | if (failures > 0) { 31 | e(new Error(`${failures} tests failed.`)); 32 | } else { 33 | c(); 34 | } 35 | }); 36 | } catch (err) { 37 | e(err); 38 | } 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /test/multi-module-suite/projectView.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as assert from "assert"; 5 | import { contextManager, DependencyExplorer, 6 | FileNode, 7 | ProjectNode } from "../../extension.bundle"; 8 | import { printNodes, setupTestEnv } from "../shared"; 9 | 10 | // tslint:disable: only-arrow-functions 11 | suite("Multi Module Tests", () => { 12 | 13 | suiteSetup(setupTestEnv); 14 | 15 | test("Can open module with name equal or longer than folder name correctly", async function() { 16 | const explorer = DependencyExplorer.getInstance(contextManager.context); 17 | 18 | const roots = await explorer.dataProvider.getChildren(); 19 | const nestedProjectNode = roots?.find(project => 20 | project instanceof ProjectNode && project.name === 'de.myorg.myservice.level1') as ProjectNode; 21 | 22 | const projectChildren = await nestedProjectNode.getChildren(); 23 | assert.ok(!!projectChildren.find(child => child instanceof FileNode && child.path?.endsWith('level1/pom.xml'), `Expected to find FileNode with level1 pom.xml in:\n${printNodes(projectChildren)}`)); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/multi-module/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.dependency.packagePresentation": "flat", 3 | "java.import.generatesMetadataFilesAtProjectRoot": false 4 | } -------------------------------------------------------------------------------- /test/multi-module/level1/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | fvclaus 6 | de.myorg.myservice 7 | 1.0-SNAPSHOT 8 | 9 | de.myorg.myservice.level1 10 | jar 11 | 1.0-SNAPSHOT 12 | http://maven.apache.org 13 | 14 | 15 | junit 16 | junit 17 | 3.8.1 18 | test 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/multi-module/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | fvclaus 5 | de.myorg.myservice 6 | pom 7 | 1.0-SNAPSHOT 8 | http://maven.apache.org 9 | 10 | level1 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/multi-module/src/main/java/fvclaus/App.java: -------------------------------------------------------------------------------- 1 | package fvclaus; 2 | 3 | /** 4 | * Hello world! 5 | * 6 | */ 7 | public class App 8 | { 9 | public static void main( String[] args ) 10 | { 11 | System.out.println( "Hello World!" ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/multi-module/src/test/java/fvclaus/AppTest.java: -------------------------------------------------------------------------------- 1 | package fvclaus; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | extends TestCase 12 | { 13 | /** 14 | * Create the test case 15 | * 16 | * @param testName name of the test case 17 | */ 18 | public AppTest( String testName ) 19 | { 20 | super( testName ); 21 | } 22 | 23 | /** 24 | * @return the suite of tests being tested 25 | */ 26 | public static Test suite() 27 | { 28 | return new TestSuite( AppTest.class ); 29 | } 30 | 31 | /** 32 | * Rigourous Test :-) 33 | */ 34 | public void testApp() 35 | { 36 | assertTrue( true ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/multiple/multiple-project.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "..\\simple" 5 | }, 6 | { 7 | "path": "..\\maven" 8 | }, 9 | { 10 | "path": "..\\gradle" 11 | } 12 | ], 13 | "settings": {} 14 | } 15 | -------------------------------------------------------------------------------- /test/shared.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as path from "path"; 5 | import { extensions, Uri } from "vscode"; 6 | import { DataNode, PackageNode } from "../extension.bundle"; 7 | import { ExplorerNode } from "../src/views/explorerNode"; 8 | 9 | export namespace Uris { 10 | // Simple Project 11 | export const SIMPLE_PROJECT_NODE = truePath("simple"); 12 | export const SIMPLE_MAIN_PACKAGE = truePath("simple", "src", "main", "java"); 13 | export const SIMPLE_MAIN_CLASS = truePath("simple", "src", "main", "java", "HelloWorld.java"); 14 | 15 | // Maven Project 16 | export const MAVEN_PROJECT_NODE = truePath("maven"); 17 | export const MAVEN_MAIN_PACKAGE = truePath("maven", "src", "main", "java"); 18 | export const MAVEN_TEST_PACKAGE = truePath("maven", "src", "test", "java"); 19 | export const MAVEN_MAIN_SUBPACKAGE = truePath("maven", "src", "main", "java", "com", "mycompany", "app"); 20 | export const MAVEN_TEST_SUBPACKAGE = truePath("maven", "src", "test", "java", "com", "mycompany", "app"); 21 | export const MAVEN_MAIN_CLASS = truePath("maven", "src", "main", "java", "com", "mycompany", "app", "App.java"); 22 | export const MAVEN_TEST_CLASS = truePath("maven", "src", "test", "java", "com", "mycompany", "app", "AppTest.java"); 23 | 24 | // Gradle Project 25 | export const GRADLE_PROJECT_NODE = truePath("gradle"); 26 | export const GRADLE_MAIN_PACKAGE = truePath("gradle", "src", "main", "java"); 27 | export const GRADLE_MAIN_CLASS = truePath("gradle", "src", "main", "java", "GradleTest.java"); 28 | } 29 | 30 | export function fsPath(node: DataNode): string { 31 | if (!node.uri) { 32 | return ""; 33 | } 34 | return path.resolve(Uri.parse(node.uri).fsPath); 35 | } 36 | 37 | export function truePath(...paths: string[]) { 38 | const basePath = path.join(__dirname, "..", "..", "test"); 39 | return path.resolve(path.join(basePath, ...paths)); 40 | } 41 | 42 | export async function setupTestEnv() { 43 | await extensions.getExtension("redhat.java")!.activate(); 44 | await extensions.getExtension("vscjava.vscode-java-dependency")!.activate(); 45 | } 46 | 47 | export function printNodes(nodes: ExplorerNode[]) { 48 | return nodes.map(node => { 49 | if (node instanceof DataNode) { 50 | return `DataNode: ${node.name}`; 51 | } 52 | if (node instanceof PackageNode) { 53 | return `PackageNode: ${node.name}`; 54 | } 55 | return `UnknownNode: ${node.constructor.name}`; 56 | }).join('\n'); 57 | } 58 | -------------------------------------------------------------------------------- /test/simple-suite/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as glob from "glob"; 5 | import * as Mocha from "mocha"; 6 | import * as path from "path"; 7 | 8 | export function run(): Promise { 9 | // Create the mocha test 10 | const mocha = new Mocha({ 11 | ui: "tdd", 12 | color: true, 13 | timeout: 1 * 60 * 1000, 14 | }); 15 | 16 | const testsRoot = __dirname; 17 | 18 | return new Promise((c, e) => { 19 | glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { 20 | if (err) { 21 | return e(err); 22 | } 23 | 24 | // Add files to the test suite 25 | files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); 26 | 27 | try { 28 | // Run the mocha test 29 | mocha.run((failures) => { 30 | if (failures > 0) { 31 | e(new Error(`${failures} tests failed.`)); 32 | } else { 33 | c(); 34 | } 35 | }); 36 | } catch (err) { 37 | e(err); 38 | } 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /test/simple-suite/projectView.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as assert from "assert"; 5 | import { ContainerNode, contextManager, DependencyExplorer, 6 | PackageRootNode, PrimaryTypeNode, ProjectNode } from "../../extension.bundle"; 7 | import { fsPath, setupTestEnv, Uris } from "../shared"; 8 | 9 | // tslint:disable: only-arrow-functions 10 | suite("Simple Project View Tests", () => { 11 | 12 | suiteSetup(setupTestEnv); 13 | 14 | test("Can node render correctly", async function() { 15 | const explorer = DependencyExplorer.getInstance(contextManager.context); 16 | 17 | // validate root nodes 18 | const roots = await explorer.dataProvider.getChildren(); 19 | assert.equal(roots?.length, 1, "Number of root node should be 1"); 20 | const projectNode = roots![0] as ProjectNode; 21 | assert.equal(projectNode.name, "1.helloworld", "Project name should be \"1.helloworld\""); 22 | 23 | // validate package root/dependency nodes 24 | const projectChildren = await projectNode.getChildren(); 25 | assert.equal(projectChildren.length, 6, "Number of children nodes should be 6"); 26 | const mainPackage = projectChildren[0] as PackageRootNode; 27 | assert.equal(mainPackage.name, "src/main/java", "Package name should be \"src/main/java\""); 28 | const systemLibrary = projectChildren[1] as ContainerNode; 29 | // only match prefix of system library since JDK version may differ 30 | assert.ok(systemLibrary.name.startsWith("JRE System Library"), "Container name should start with JRE System Library"); 31 | 32 | // validate innermost layer nodes 33 | const mainClasses = await mainPackage.getChildren(); 34 | assert.equal(mainClasses.length, 1, "Number of main classes should be 1"); 35 | const mainClass = mainClasses[0] as PrimaryTypeNode; 36 | assert.equal(mainClass.name, "HelloWorld", "Name of main class should be \"HelloWorld\""); 37 | }); 38 | 39 | test("Can node have correct uri", async function() { 40 | const explorer = DependencyExplorer.getInstance(contextManager.context); 41 | 42 | const projectNode = (await explorer.dataProvider.getChildren())![0] as ProjectNode; 43 | const mainPackage = (await projectNode.getChildren())[0] as PackageRootNode; 44 | const mainClass = (await mainPackage.getChildren())[0] as PrimaryTypeNode; 45 | 46 | assert.equal(fsPath(projectNode), Uris.SIMPLE_PROJECT_NODE, "Project uri incorrect"); 47 | assert.equal(fsPath(mainPackage), Uris.SIMPLE_MAIN_PACKAGE, "Main root package uri incorrect"); 48 | assert.equal(fsPath(mainClass), Uris.SIMPLE_MAIN_CLASS, "Main class uri incorrect"); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/simple/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/simple/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1.helloworld 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/simple/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "java", 6 | "name": "Java Debug", 7 | "request": "launch", 8 | "cwd": "${workspaceRoot}", 9 | "stopOnEntry": true, 10 | "mainClass": "HelloWorld", 11 | "sourcePaths": [ 12 | "${workspaceRoot}/src/main/java" 13 | ], 14 | "options": [] 15 | }, 16 | { 17 | "type": "java", 18 | "name": "Java Debug (Attach)", 19 | "request": "attach", 20 | "hostName": "localhost", 21 | "port": 0, 22 | "attachTimeout": 30000, 23 | "cwd": "${workspaceRoot}", 24 | "sourcePaths": [ 25 | "${workspaceRoot}" 26 | ] 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /test/simple/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.server.launchMode": "Standard" 3 | } 4 | -------------------------------------------------------------------------------- /test/simple/src/main/java/HelloWorld.java: -------------------------------------------------------------------------------- 1 | public class HelloWorld { 2 | public static void main(String[] args) { 3 | System.out.print("hello"); 4 | if (args != null && args.length > 0) { 5 | for (String arg : args) { 6 | System.out.print(" " + arg); 7 | } 8 | System.out.println(); 9 | } else { 10 | System.out.println(" world"); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/suite/buildTask.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as assert from "assert"; 5 | import { Task, tasks, TaskScope } from "vscode"; 6 | import { BuildTaskProvider, categorizePaths, getFinalPaths } from "../../extension.bundle"; 7 | import { setupTestEnv } from "../shared"; 8 | 9 | // tslint:disable: only-arrow-functions 10 | // tslint:disable: no-object-literal-type-assertion 11 | // tslint:disable: no-invalid-template-strings 12 | 13 | suite("Build Task Tests", () => { 14 | 15 | suiteSetup(setupTestEnv); 16 | 17 | test("test providing default build task", async function() { 18 | this.timeout(60 * 1000 * 3); 19 | const vscodeTasks: Task[] = await tasks.fetchTasks(); 20 | const exportJarTask: Task | undefined = vscodeTasks.find((t: Task) => { 21 | return t.name === BuildTaskProvider.defaultTaskName 22 | && t.source === BuildTaskProvider.type; 23 | }); 24 | assert.ok(exportJarTask !== undefined); 25 | }); 26 | 27 | test("test categorizePaths()", async function() { 28 | const [includes, excludes, invalid] = categorizePaths([ 29 | BuildTaskProvider.workspace, 30 | "a/b/c", 31 | "!foo" 32 | ], TaskScope.Workspace); 33 | assert.deepStrictEqual(includes.length, 2); 34 | assert.deepStrictEqual(excludes.length, 1); 35 | assert.deepStrictEqual(invalid.length, 0); 36 | }); 37 | 38 | test("test getFinalPaths() 1", async function() { 39 | const [result, invalid] = getFinalPaths([ 40 | BuildTaskProvider.workspace, 41 | "a/b/c", 42 | ], [ 43 | "foo/bar", 44 | ], [ 45 | "a/b/c", 46 | "foo/bar", 47 | "test/path" 48 | ]); 49 | assert.deepStrictEqual(result.length, 2); 50 | assert.deepStrictEqual(invalid.length, 0); 51 | }); 52 | 53 | test("test getFinalPaths() 2", async function() { 54 | const [result, invalid] = getFinalPaths([ 55 | "a/b/c", 56 | "non/exist2" 57 | ], [ 58 | "foo/bar", 59 | "non/exist" 60 | ], [ 61 | "a/b/c", 62 | "foo/bar", 63 | "test/path" 64 | ]); 65 | assert.deepStrictEqual(result.length, 1); 66 | assert.deepStrictEqual(invalid.length, 1); 67 | assert.deepStrictEqual(invalid[0], "non/exist2"); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as assert from "assert"; 5 | import * as vscode from "vscode"; 6 | 7 | // tslint:disable: only-arrow-functions 8 | // Defines a Mocha test suite to group tests of similar kind together 9 | suite("Extension Tests", () => { 10 | 11 | test("Extension should be present", () => { 12 | assert.ok(vscode.extensions.getExtension("vscjava.vscode-java-dependency")); 13 | }); 14 | 15 | test("should activate", async function() { 16 | await vscode.extensions.getExtension("vscjava.vscode-java-dependency")!.activate(); 17 | assert.ok(true); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/suite/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as glob from "glob"; 5 | import * as Mocha from "mocha"; 6 | import * as path from "path"; 7 | 8 | export function run(): Promise { 9 | // Create the mocha test 10 | const mocha = new Mocha({ 11 | ui: "tdd", 12 | color: true, 13 | timeout: 1 * 60 * 1000, 14 | }); 15 | 16 | const testsRoot = __dirname; 17 | 18 | return new Promise((c, e) => { 19 | glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { 20 | if (err) { 21 | return e(err); 22 | } 23 | 24 | // Add files to the test suite 25 | files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); 26 | 27 | try { 28 | // Run the mocha test 29 | mocha.run((failures) => { 30 | if (failures > 0) { 31 | e(new Error(`${failures} tests failed.`)); 32 | } else { 33 | c(); 34 | } 35 | }); 36 | } catch (err) { 37 | e(err); 38 | } 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /test/ui/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | import * as fse from "fs-extra"; 5 | import * as path from "path"; 6 | import * as semver from "semver"; 7 | import { ExTester } from "vscode-extension-tester"; 8 | 9 | /* tslint:disable:no-console */ 10 | async function main(): Promise { 11 | try { 12 | // Run UI command tests 13 | const packageContent = await fse.readFile(path.join(__dirname, "..", "..", "..", "package.json")); 14 | const packageJSON = JSON.parse(packageContent.toString()); 15 | let vscodeVersion = packageJSON.engines.vscode; 16 | if (!vscodeVersion) { 17 | console.log("No valid version of VSCode engine was found in package.json"); 18 | process.exit(1); 19 | } 20 | vscodeVersion = semver.minVersion(vscodeVersion); 21 | const version = vscodeVersion.version; 22 | const testPath = path.join(__dirname, "command.test.js"); 23 | const exTester = new ExTester(); 24 | await exTester.downloadCode(version); 25 | await exTester.installVsix(); 26 | await exTester.installFromMarketplace("redhat.java"); 27 | await exTester.downloadChromeDriver(version); 28 | await exTester.setupRequirements({vscodeVersion: version}); 29 | process.exit(await exTester.runTests(testPath, {vscodeVersion: version, resources: []})); 30 | } catch (err) { 31 | console.log(err); 32 | process.exit(1); 33 | } 34 | } 35 | 36 | main(); 37 | -------------------------------------------------------------------------------- /test/util.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | export async function sleep(time: number) { 5 | await new Promise((resolve) => setTimeout(resolve, time)); 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6", 8 | "DOM" 9 | ], 10 | "sourceMap": true, 11 | "rootDir": ".", 12 | "noUnusedLocals": true, 13 | "noImplicitThis": true, 14 | "noImplicitAny": true, 15 | "noImplicitReturns": true, 16 | "noUnusedParameters": true, 17 | "strictNullChecks": true, 18 | "alwaysStrict": true 19 | }, 20 | "exclude": [ 21 | "node_modules", 22 | ".vscode-test", 23 | "test-resources" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:latest", 3 | "rules": { 4 | "variable-name": [ 5 | true, 6 | "allow-leading-underscore" 7 | ], 8 | "no-unused-expression": true, 9 | "no-duplicate-variable": true, 10 | "max-classes-per-file": false, 11 | "no-implicit-dependencies": [ 12 | false, 13 | "dev" 14 | ], 15 | "no-empty": false, 16 | "object-literal-sort-keys": false, 17 | "curly": true, 18 | "class-name": true, 19 | "whitespace": [ 20 | true, 21 | "check-branch", 22 | "check-decl", 23 | "check-operator", 24 | "check-module", 25 | "check-separator", 26 | "check-type" 27 | ], 28 | "semicolon": [ 29 | true 30 | ], 31 | "triple-equals": true, 32 | "max-line-length": [ 33 | true, 34 | 150 35 | ], 36 | "no-angle-bracket-type-assertion": false, 37 | "no-console": [ 38 | true, 39 | "log", 40 | "error" 41 | ], 42 | "no-namespace": false, 43 | "prefer-conditional-expression": false 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 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 | //@ts-check 6 | 'use strict'; 7 | const path = require('path'); 8 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 9 | 10 | /**@type {import('webpack').Configuration}*/ 11 | const config = { 12 | target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ 13 | node: { 14 | __dirname: false, 15 | __filename: false, 16 | }, 17 | entry: { 18 | "extension.bundle": "./extension.bundle.ts", 19 | }, // 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: '[name].js', 23 | libraryTarget: "commonjs2", 24 | devtoolModuleFilenameTemplate: "../[resource-path]", 25 | }, 26 | plugins: [ 27 | // Copy files to dist folder where the runtime can find them 28 | // @ts-ignore 29 | new CopyWebpackPlugin({ 30 | patterns: [ 31 | { 32 | from: path.join(__dirname, 'out', 'test'), 33 | to: path.join(__dirname, 'dist', 'test') 34 | }, 35 | ] 36 | }), 37 | ], 38 | externals: { 39 | 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/ 40 | 'applicationinsights-native-metrics': 'commonjs applicationinsights-native-metrics' // https://github.com/microsoft/vscode-extension-telemetry/issues/41#issuecomment-598852991 41 | }, 42 | devtool: 'source-map', 43 | resolve: { // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 44 | extensions: ['.ts', '.js'], 45 | }, 46 | module: { 47 | rules: [{ 48 | test: /\.ts$/, 49 | exclude: /node_modules/, 50 | use: [{ 51 | loader: 'ts-loader', 52 | }] 53 | }] 54 | }, 55 | } 56 | module.exports = config; 57 | --------------------------------------------------------------------------------