├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml └── workflows │ ├── build-and-test.yml │ ├── build-doc.yml │ └── codeql.yml ├── .gitignore ├── .vscode-test.mjs ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── doc-requirements.txt ├── doc └── FEATURES.md ├── docs ├── .pages ├── Features │ └── index.md ├── _assets │ └── favicon.png ├── _hooks │ └── changelog.py ├── _stylesheets │ └── extra.css ├── changelog.md └── index.md ├── jest.config.js ├── mkdocs.yml ├── package.json ├── resources ├── icon │ ├── conan_io_color.png │ ├── conan_io_gray.png │ ├── dark │ │ ├── add.png │ │ ├── binary.png │ │ ├── copy.png │ │ ├── disable.png │ │ ├── edit.png │ │ ├── enable.png │ │ ├── filter.png │ │ ├── filter_clear.png │ │ ├── goto.png │ │ ├── info.png │ │ ├── off.png │ │ ├── on.png │ │ ├── profile.png │ │ ├── refresh.png │ │ ├── remote_off.png │ │ ├── remote_on.png │ │ ├── remove.png │ │ ├── rename.png │ │ ├── url.png │ │ └── vscode.png │ ├── light │ │ ├── add.png │ │ ├── binary.png │ │ ├── copy.png │ │ ├── disable.png │ │ ├── edit.png │ │ ├── enable.png │ │ ├── filter.png │ │ ├── filter_clear.png │ │ ├── goto.png │ │ ├── info.png │ │ ├── off.png │ │ ├── on.png │ │ ├── profile.png │ │ ├── refresh.png │ │ ├── remote_off.png │ │ ├── remote_on.png │ │ ├── remove.png │ │ ├── rename.png │ │ ├── url.png │ │ └── vscode.png │ ├── package.png │ ├── package_dirty.png │ ├── package_revision.png │ ├── recipe.png │ ├── recipe_editable.png │ ├── vsconan-explorer-logo.png │ ├── vsconan-extension-logo.png │ └── vsconan-logo.png ├── img │ ├── conan_package_treeview.png │ ├── conan_profile_treeview.png │ ├── conan_recipe_treeview.png │ ├── conan_remote_treeview.png │ ├── demo_explorer.gif │ ├── demo_workspace.gif │ ├── prompt_conan_project.png │ └── vsconan_gui_settings.png └── print_env.py ├── src ├── conans │ ├── api │ │ ├── base │ │ │ └── conanAPI.ts │ │ └── conanAPIManager.ts │ ├── command │ │ ├── commandBuilder.ts │ │ ├── commandBuilderFactory.ts │ │ └── configCommand.ts │ ├── conan │ │ ├── api │ │ │ └── conanAPI.ts │ │ └── commandBuilder.ts │ ├── conan2 │ │ ├── api │ │ │ └── conanAPI.ts │ │ └── commandBuilder.ts │ ├── model │ │ ├── conanPackage.ts │ │ ├── conanPackageRevision.ts │ │ ├── conanProfile.ts │ │ ├── conanRecipe.ts │ │ └── conanRemote.ts │ └── workspace │ │ └── configWorkspace.ts ├── extension.ts ├── extension │ ├── disposable.ts │ ├── manager │ │ ├── explorer │ │ │ ├── conanCache.ts │ │ │ ├── conanProfile.ts │ │ │ └── conanRemote.ts │ │ ├── extensionManager.ts │ │ ├── vsconanWorkspace.ts │ │ └── workspaceEnvironment.ts │ ├── settings │ │ ├── model.ts │ │ ├── settingsManager.ts │ │ └── settingsPropertyManager.ts │ └── ui │ │ └── treeview │ │ ├── conanPackageProvider.ts │ │ ├── conanPackageRevisionProvider.ts │ │ ├── conanProfileProvider.ts │ │ ├── conanRecipeProvider.ts │ │ └── conanRemoteProvider.ts └── utils │ ├── constants.ts │ └── utils.ts ├── test ├── conan │ ├── apiManager.test.ts │ ├── commandBuilder │ │ ├── conan1Build.test.ts │ │ ├── conan1Create.test.ts │ │ ├── conan1Install.test.ts │ │ ├── conan1Package.test.ts │ │ ├── conan1PackageExport.test.ts │ │ ├── conan1Source.test.ts │ │ └── conan2Create.test.ts │ ├── commandBuilderFactory.test.ts │ ├── configCommand.test.ts │ └── readEnv.test.ts ├── mocks │ └── vscode.ts └── utils.test.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/naming-convention": "warn", 13 | "@typescript-eslint/semi": "warn", 14 | "curly": "warn", 15 | "eqeqeq": "warn", 16 | "no-throw-literal": "warn", 17 | "semi": "off" 18 | }, 19 | "ignorePatterns": [ 20 | "out", 21 | "dist", 22 | "**/*.d.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: "🐞 Bug Report" 2 | description: "Report a bug to help improve the project." 3 | title: "[Bug]: " 4 | labels: ["bug", "needs-triage"] 5 | assignees: [] 6 | 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: "### 🐞 Please fill out the details below to report a bug." 11 | 12 | - type: input 13 | id: summary 14 | attributes: 15 | label: "Issue Summary" 16 | description: "Briefly describe the bug (1-2 sentences)." 17 | placeholder: "Example: The login button does not respond when clicked." 18 | validations: 19 | required: true 20 | 21 | - type: textarea 22 | id: actual_behavior 23 | attributes: 24 | label: "Actual Behavior" 25 | description: "Describe what actually happens, including error messages." 26 | placeholder: "Example: The login button does nothing and shows a console error." 27 | validations: 28 | required: true 29 | 30 | - type: textarea 31 | id: steps 32 | attributes: 33 | label: "Steps to Reproduce" 34 | description: "Provide step-by-step instructions to reproduce the issue." 35 | placeholder: | 36 | 1. Go to '...' 37 | 2. Click on '...' 38 | 3. Observe '...' 39 | validations: 40 | required: true 41 | 42 | - type: textarea 43 | id: expected_behavior 44 | attributes: 45 | label: "Expected Behavior" 46 | description: "Describe what should have happened." 47 | placeholder: "Example: The login button should redirect to the dashboard." 48 | validations: 49 | required: true 50 | 51 | - type: input 52 | id: technical_details 53 | attributes: 54 | label: "Technical Details" 55 | description: "Provide any relevant technical details." 56 | placeholder: "Example: OS version, extension version" 57 | validations: 58 | required: true 59 | 60 | - type: textarea 61 | id: logs 62 | attributes: 63 | label: "Error Logs (if any)" 64 | description: "Paste any relevant logs or console errors." 65 | render: shell 66 | 67 | - type: textarea 68 | id: additional_information 69 | attributes: 70 | label: "Additional Information" 71 | description: "Any other relevant details?" -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: ❓ Questions & Discussions 4 | url: https://github.com/afri-bit/vsconan/discussions 5 | about: Ask a question, share ideas, or discuss features. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: "✨ Feature Request" 2 | description: "Suggest a new feature or improvement." 3 | title: "[Feature]: " 4 | labels: ["enhancement", "feature request"] 5 | assignees: [] 6 | 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: "### ✨ Thank you for your feature request! Please fill out the details below." 11 | 12 | - type: input 13 | id: summary 14 | attributes: 15 | label: "Feature Summary" 16 | description: "Briefly describe the new feature or improvement." 17 | placeholder: "Example: Add dark mode support." 18 | validations: 19 | required: true 20 | 21 | - type: textarea 22 | id: problem 23 | attributes: 24 | label: "Problem Statement" 25 | description: "Describe the problem this feature will solve. Why is it needed?" 26 | placeholder: "Example: The current interface is too bright at night, making it difficult to use." 27 | validations: 28 | required: true 29 | 30 | - type: textarea 31 | id: solution 32 | attributes: 33 | label: "Proposed Solution" 34 | description: "Describe how you think this feature should be implemented." 35 | placeholder: "Example: Add a toggle button in settings to switch between light and dark mode." 36 | validations: 37 | required: true 38 | 39 | - type: textarea 40 | id: alternatives 41 | attributes: 42 | label: "Alternatives Considered" 43 | description: "Have you considered any alternative solutions or workarounds?" 44 | placeholder: "Example: Using a browser extension for dark mode, but it doesn't work well." 45 | 46 | - type: dropdown 47 | id: priority 48 | attributes: 49 | label: "Priority Level" 50 | description: "How important is this feature?" 51 | options: 52 | - Nice to have 53 | - Low 54 | - Medium 55 | - High 56 | - Critical 57 | default: 2 58 | validations: 59 | required: true 60 | 61 | - type: textarea 62 | id: additional_information 63 | attributes: 64 | label: "Additional Information" 65 | description: "Provide any additional details, mockups, or links that may help." 66 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: 'Build & Test' 2 | 3 | on: 4 | push: 5 | 6 | pull_request: 7 | branches: 8 | - main 9 | - develop 10 | 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v1 18 | - uses: actions/setup-node@v1 19 | with: 20 | node-version: '22.x' 21 | - name: Install and setup conan 22 | run: pip install conan && conan profile detect 23 | - run: npm install 24 | - run: npm run compile 25 | - run: npm run test 26 | -------------------------------------------------------------------------------- /.github/workflows/build-doc.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v3 16 | 17 | - name: Set up Python 18 | uses: actions/setup-python@v4 19 | with: 20 | python-version: '3.10' 21 | 22 | - name: Install dependencies 23 | run: | 24 | python -m pip install --upgrade pip 25 | pip install -r doc-requirements.txt 26 | 27 | - name: Build MkDocs site 28 | run: mkdocs build 29 | 30 | - name: Deploy to GitHub Pages 31 | uses: peaceiris/actions-gh-pages@v3 32 | with: 33 | github_token: ${{ secrets.GH_TOKEN }} 34 | publish_dir: ./site 35 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: CodeQL Analysis 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | # schedule: 9 | # - cron: '0 12 * * 1' # Runs every Monday at 12:00 UTC 10 | workflow_dispatch: 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze TypeScript Code 15 | runs-on: ubuntu-latest 16 | 17 | permissions: 18 | security-events: write 19 | actions: read 20 | contents: read 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | language: [ 'typescript' ] 26 | 27 | steps: 28 | - name: Checkout repository 29 | uses: actions/checkout@v4 30 | 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v3 33 | with: 34 | languages: ${{ matrix.language }} 35 | 36 | - name: Autobuild 37 | uses: github/codeql-action/autobuild@v3 38 | 39 | - name: Perform CodeQL Analysis 40 | uses: github/codeql-action/analyze@v3 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | .vscode-test/ 5 | *.vsix 6 | package-lock.json 7 | *.db* 8 | build/ 9 | dist/ 10 | out/ 11 | test/**/*.js 12 | *.js.map 13 | 14 | .bak/ 15 | coverage 16 | 17 | .DS_Store 18 | 19 | # Python things for mkdocs 20 | .venv/ 21 | .idea 22 | bak 23 | *.egg-info 24 | __pycache__/ 25 | *.pyc 26 | dist/ 27 | 28 | test/conan/conanfile.py -------------------------------------------------------------------------------- /.vscode-test.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@vscode/test-cli'; 2 | 3 | export default defineConfig({ 4 | files: 'out/test/**/*.test.js', 5 | }); 6 | -------------------------------------------------------------------------------- /.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 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ], 15 | "outFiles": [ 16 | "${workspaceFolder}/out/**/*.js" 17 | ], 18 | "preLaunchTask": "${defaultBuildTask}" 19 | }, 20 | { 21 | "name": "Extension Tests", 22 | "type": "extensionHost", 23 | "request": "launch", 24 | "args": [ 25 | "--disable-extensions", 26 | "--extensionDevelopmentPath=${workspaceFolder}", 27 | "--extensionTestsPath=${workspaceFolder}/out/test/index", 28 | ], 29 | "outFiles": [ 30 | "${workspaceFolder}/out/test/**/*.js" 31 | ], 32 | "preLaunchTask": "${defaultBuildTask}" 33 | }, 34 | ] 35 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.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": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | .yarnrc 7 | vsc-extension-quickstart.md 8 | resources/**/*.gif 9 | **/tsconfig.json 10 | **/.eslintrc.json 11 | **/*.map 12 | **/*.ts 13 | node_modules/*/*.md 14 | out/**/*.d.ts 15 | out/**/*.js.map 16 | out/*.d.ts 17 | out/*.js.map 18 | jest.config.js 19 | test/** 20 | .bak/** 21 | 22 | .venv/** 23 | docs/** 24 | doc/** */ 25 | mkdocs.yml 26 | doc-requirements.txt 27 | CODE_OF_CONDUCT.md 28 | CONTRIBUTING.md 29 | .vscode-test.mjs -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 1.4.0 - 2025-03-04 4 | 5 | ### Added 6 | 7 | * [#50](https://github.com/afri-bit/vsconan/issues/50) Conan Package Revision directory structure 8 | Show content of package revision in the treeview, basically recreating the file explorer for the conan package revision 9 | 10 | ### Changed 11 | 12 | * [#43](https://github.com/afri-bit/vsconan/issues/43) Improved the UX on conan profile selection 13 | Removed the edit button, and replace it with native VS code click interface. User can open the conan profile in the editor by double clicking / single clicking (depends on the configuration.) 14 | 15 | ## 1.3.2 - 2025-02-11 16 | 17 | ### Fixed 18 | 19 | * [#49](https://github.com/afri-bit/vsconan/issues/49) Fix path issue with whitespaces 20 | 21 | ## 1.3.1 - 2024-10-22 22 | 23 | ### Fixed 24 | 25 | * Fix activation of BuildEnv/RunEnv 26 | 27 | ## 1.3.0 - 2024-09-10 28 | 29 | * [#36](https://github.com/afri-bit/vsconan/issues/36) Support the application of Conan's BuildEnv/RunEnv (Conan 2 only) 30 | * [#27](https://github.com/afri-bit/vsconan/issues/27) Added button in the status bar for frequently used conan commands 31 | 32 | ## 1.2.0 - 2024-08-03 33 | 34 | ### Added 35 | 36 | * [#39](https://github.com/afri-bit/vsconan/pull/39) Support detection of python interpreter by ms-python.python extension 37 | 38 | ## 1.1.0 - 2024-07-07 39 | 40 | ### Added 41 | 42 | * [#38](https://github.com/afri-bit/vsconan/issues/38) Support whitespace for project and configuration path 43 | You can now use whitespace in your configuration file and *VSConan* can still parse the path and use it for executing conan command. 44 | > Additional to this feature, internal the command builder is changed to separate the command and arguments. For further detail of the issue please refer to [#38](https://github.com/afri-bit/vsconan/issues/38). 45 | *This change should not affect the current configuration file.* 46 | 47 | Thanks to [torsknod-the-caridian](https://github.com/torsknod-the-caridian). 48 | 49 | ## 1.0.1 - 2024-02-04 50 | 51 | ### Changed 52 | 53 | * [#35](https://github.com/afri-bit/vsconan/issues/35) Extension cannot be activated after installation 54 | Due to missing dependencies or unability to find the dependencies, the extension cannot be started. So I replaced the small function that I used from this dependency with internal utility function. 55 | > Midnight programming mistake :P 56 | 57 | 58 | ## 1.0.0 - 2024-02-03 59 | 60 | ### Breaking Changes 61 | 62 | * Following settings in `settings.json` are **OBSOLETE** 63 | * `vsconan.general.pythonInterpreter` 64 | * `vsconan.general.conanExecutable` 65 | * `vsconan.general.conanExecutionMode` 66 | 67 | Instead, you can define multiple profiles according to your need in the `settings.json`. See example below: 68 | 69 | ```json 70 | "vsconan.conan.profile.configurations": { 71 | "myprofile_1": { 72 | "conanVersion": "1", 73 | "conanPythonInterpreter": "python", 74 | "conanExecutable": "conan", 75 | "conanExecutionMode": "pythonInterpreter", 76 | }, 77 | "myprofile_2": { 78 | "conanVersion": "2", 79 | "conanPythonInterpreter": "/home/user/.venv/bin/conan", 80 | "conanExecutable": "/home/user/.venv/bin/conan", 81 | "conanExecutionMode": "conanExecutable", 82 | "conanUserHome": "/home/user/your/path/to/.conan2" 83 | } 84 | }, 85 | "vsconan.conan.profile.default": "myprofile_2", 86 | ``` 87 | 88 | Using `vsconan.conan.profile.default` you can switch the profile easily, in case you have multiple conan setup or multiple python virtual environments with different conan versions. `conanUserHome` is optional parameter, in case you want to have a different location for `conan` home folder. 89 | 90 | * The workspace configuration to execute conan command, such as `build`, `install`, etc., is slightly changed, but it has a big impact to your workflow / usage of this configuration. 91 | > The `python` attribute is no longer available! 92 | 93 | Instead of using hard coded `python` path inside this json file, it will rely on the selected conan configuration profile. So with this change, you can use the same json file but using in the different conan version (Easy switch between conan 1 and 2). 94 | 95 | ```json 96 | { 97 | "commandContainer": { 98 | "create": [ 99 | { 100 | "name": "create", 101 | "description": "Create command", 102 | "detail": "Create command detail", 103 | "conanRecipe": "conanfile.py", 104 | "profile": "default", 105 | "user": "", 106 | "channel": "", 107 | "args": [] 108 | } 109 | ], 110 | "install": [ 111 | { 112 | "name": "install", 113 | "description": "Install command", 114 | "detail": "Install command detail", 115 | "conanRecipe": "conanfile.py", 116 | "installFolder": "install", 117 | "profile": "default", 118 | "user": "", 119 | "channel": "", 120 | "args": [] 121 | } 122 | ], 123 | . 124 | . 125 | . 126 | . 127 | } 128 | } 129 | ``` 130 | 131 | Due to the fact that there are some differences in the commands between conan 1 and 2, some attributes are added to some commands, that might not be used in one conan version or the other. 132 | Following example might give you clarity. 133 | 134 | * Conan 1 - `conan source` 135 | ```shell 136 | optional arguments: 137 | -h, --help 138 | -sf SOURCE_FOLDER, --source-folder SOURCE_FOLDER 139 | -if INSTALL_FOLDER, --install-folder INSTALL_FOLDER 140 | ``` 141 | 142 | * Conan 2 - `conan source` 143 | ```shell 144 | optional arguments: 145 | --name NAME 146 | --version VERSION 147 | --user USER 148 | --channel CHANNEL 149 | ``` 150 | 151 | ### Added 152 | * Conan 2 - Browsing the recipe with UI 153 | * Delete recipe 154 | * Open in VSCode 155 | * Open in explorer 156 | * Conan 2 - Browsing the packages with UI 157 | * Delete Package 158 | * Open in VSCode 159 | * Open in explorer 160 | * Conan 2 - Browsing the package revisions with UI 161 | * Delete package revision 162 | * Open in VSCode 163 | * Open in explorer 164 | * Conan 2 - Working with remotes with tree view 165 | * Same functionality as conan1 166 | * Conan 2 - Working with profiles with Tree view 167 | * Same functionality as conan1 168 | * Multiple profile definition for conan configuration in `settings.json` 169 | * Easy switch between conan configuration profile using status bar 170 | * Status bar view of selected conan configuration profile 171 | * Added new treeview for package revision (Only meant for conan2) 172 | 173 | ### Changed 174 | * New color palette for the VSConan logo :) Adapted the color according to the new official conan logo (roughly) 175 | 176 | ## 0.4.0 - 2022-09-11 177 | 178 | ### Added 179 | * [#24](https://github.com/afri-bit/vsconan/issues/24) Open different folders under recipe folder 180 | * User has the possibility to access different folders that are located under the recipe folder itself, such as `build`, `dl`, `source`, etc. 181 | * User can open the folder either in the explorer or in a new VS Code window. The option to open the folders can be found by using right click on the recipe item from the explorer. 182 | * [#17](https://github.com/afri-bit/vsconan/issues/17) Filter recipes based on a selected remote 183 | * [#18](https://github.com/afri-bit/vsconan/issues/18) Filter binary packages based on a selected remote 184 | 185 | ## 0.3.0 - 2022-06-06 186 | 187 | ### Added 188 | * [#16](https://github.com/afri-bit/vsconan/issues/16) Configuration in settings.json for `CONAN_USER_HOME` 189 | * Enable possibility to overwrite the pre defined `CONAN_USER_HOME` environment variable using `vsconan.general.conanUserHome` configuration option 190 | * User can set home directory to conan local cache within the VS Code using `settings.json` file 191 | 192 | ## 0.2.0 - 2022-05-30 193 | 194 | ### Added 195 | * [#10](https://github.com/afri-bit/vsconan/issues/10) Enable option to list dirty packages from a recipe 196 | * `vsconan.explorer.treeview.package.showDirtyPackage` is available to set the flag persistent 197 | * [#14](https://github.com/afri-bit/vsconan/issues/14) Support non-pip conan installation 198 | * Enable possibility for user to use the extension using alternative conan installation (e.g. conan executable) 199 | * Provide mode switch between python interpreter and conan executable (User can still use the python interpreter to execute conan CLI) 200 | * Configuration for the extension in `settings.json` 201 | * `vsconan.general.conanExecutable` 202 | * `vsconan.general.conanExecutionMode` 203 | * `vsconan.general.pythonInterpreter` 204 | * Right click option for recipe and package treeview item to copy its path 205 | * [#13](https://github.com/afri-bit/vsconan/issues/13) Managing editable packages 206 | * List editable packages in the treeview 207 | * Remove editable package via Treeview 208 | * Open editable package in VS Code 209 | * Open editable package in Explorer 210 | * Copy editable path to clipboard 211 | * Remove editable package via command and quickpick (simple option) 212 | * Add editable package from the workspace 213 | * Enable layout file input for the editable package 214 | **!!!** Currently only supporting the manual input from the user for the layout. 215 | 216 | 217 | ### Changed 218 | * The configuration for extension is migrated to official VS Code `settings.json`. Custom global `config.json` under `~/.vsconan` is now **deprecated**. 219 | 220 | ### Removed 221 | * `VSConan: Create Global Configuration (JSON)` 222 | Command to create global configuration file in your home directory 223 | * `VSConan: Open Global Configuration (JSON)` 224 | Open the global configuration file in editor 225 | 226 | ## 0.1.0 - 2022-04-21 227 | * Initial Release 228 | * Conan Explorer 229 | * Conan Recipe 230 | * Recipe information 231 | * Open in explorer 232 | * Open in VS Code 233 | * Remove recipe 234 | * Conan Binary Packages 235 | * Open in explorer 236 | * Open in VS Code 237 | * Remove binary package 238 | * Conan Profile 239 | * Add new profile 240 | * Edit profile 241 | * Open in explorer 242 | * Rename profile 243 | * Duplicate profile 244 | * Remove profile 245 | * Conan Remote 246 | * Edit `remotes.json` file in VS Code 247 | * Rename remote 248 | * Update remote URL 249 | * Enable remote 250 | * Disable remote 251 | * Remove remote 252 | * Conan Workspace 253 | * `conan create` 254 | * `conan install` 255 | * `conan build` 256 | * `conan source` 257 | * `conan package` 258 | * `conan export-pkg` 259 | * Additional Support Features 260 | * Create global configuration file 261 | * Open global configuration file 262 | * Create workspace configuration file 263 | * Open workspace configuration file -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at {{ email }}. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to VSConan 2 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: 3 | 4 | - Reporting a bug 5 | - Discussing the current state of the code 6 | - Submitting a fix 7 | - Proposing new features 8 | 9 | ## We Develop with Github 10 | We use github to host code, to track issues and feature requests, as well as accept pull requests. 11 | 12 | ## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests 13 | Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests: 14 | 15 | 1. Fork the repo and create your branch from `main`. 16 | 2. If you've added code that should be tested, add tests. 17 | 3. If you've changed APIs, update the documentation. 18 | 4. Ensure the test suite passes. 19 | 5. Make sure your code lints. 20 | 6. Issue that pull request! 21 | 22 | ## Any contributions you make will be under the MIT Software License 23 | In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. 24 | 25 | ## Report bugs using Github's [issues](https://github.com/afri-bit/vsconan/issues) 26 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/afri-bit/vsconan/issues/new), it's that easy! 27 | 28 | ## Write bug reports with detail, background, and sample code 29 | **Great Bug Reports** tend to have: 30 | 31 | - A quick summary and/or background 32 | - Steps to reproduce 33 | - Be specific! 34 | - Give sample code if you can 35 | - What you expected would happen 36 | - What actually happens 37 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) 38 | 39 | ## Coding Style 40 | * 4 spaces for indentation rather than tabs 41 | * Run `npm run lint` to confirm to our lint rules 42 | 43 | ## License 44 | By contributing, you agree that your contributions will be licensed under its MIT License. 45 | 46 | ## References 47 | This document was adapted from the open-source contribution guidelines for [Facebook's Draft]() 48 | 49 | ## Setup 50 | 51 | This repository follows the standard layout of a VS Code extension. 52 | More information can be found [here](https://code.visualstudio.com/api/get-started/extension-anatomy). 53 | 54 | ### Tests 55 | 56 | In addition to the standard setup for VS Code extension development [./test/conan/readEnv.test.ts](./test/conan/readEnv.test.ts) requires a proper Conan 2 installation. 57 | This can be achieved e.g. by using a Python virtual environment: 58 | 59 | ```sh 60 | python -m venv .venv 61 | . .venv/bin/activate 62 | # or on Windows 63 | # .venv/Scripts/activate 64 | pip install "conan>=2" 65 | ``` 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 - present afri-bit 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 | -------------------------------------------------------------------------------- /doc-requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs 2 | mkdocs-material 3 | mkdocs-awesome-pages-plugin -------------------------------------------------------------------------------- /doc/FEATURES.md: -------------------------------------------------------------------------------- 1 | # Features 2 | 3 | ## Table of Contents 4 | - [Features](#features) 5 | - [Table of Contents](#table-of-contents) 6 | - [Explorer](#explorer) 7 | - [Workspace](#workspace) 8 | - [General](#general) 9 | 10 | ## Explorer 11 | ### Conan - Recipe 12 | * Show list of conan recipe in local cache 13 | * Show list of editable package recipes 14 | * Show information about the recipe (also available for editable mode) 15 | * Open recipe in explorer (also available for editable mode) 16 | * Open recipe in VS Code (also available for editable mode) 17 | * Remove 18 | * Remove recipe from local cache 19 | * Remove editable package recipe from editable mode 20 | * Filter list of recipe based on a remote 21 | 22 | ### Conan - Package 23 | * Show list of binary packages from a recipe 24 | * Open in Explorer 25 | * Open in VS Code 26 | * Remove package 27 | * Dirty package 28 | * Show list of dirty packages 29 | * Open in Explorer 30 | * Filter list of binary packages that belong to a recipe based on a remote 31 | 32 | ### Conan - Package Revision 33 | * Show list of binary packages revision from a package 34 | * Open in Explorer 35 | * Open in VS Code 36 | * Remove package 37 | * Filter list of binary packages that belong to a recipe based on a remote 38 | 39 | ### Conan - Profile 40 | * Add new profile 41 | * Edit profile in VS Code editor 42 | * Open in Explorer 43 | * Rename profile 44 | * Duplicate profile 45 | * Remove profile 46 | 47 | ### Conan - Remote 48 | * Edit remote fils in VS Code editor 49 | * Add new remote 50 | * Rename remote 51 | * Update URL of remote 52 | * Enable / Disable remote 53 | * Remove remote 54 | 55 | ## Workspace 56 | * Execution of conan comand from workspace configuration: 57 | * create 58 | * install 59 | * build 60 | * source 61 | * package 62 | * export-pkg 63 | * Add editable package 64 | * Remove editable package 65 | * Automatic selection of Python interpreter using the ms-python.python extension 66 | * Application of Conan's buildEnv/runEnv 67 | 68 | ## General 69 | * Define multiple conan profiles inside `settings.json` that you can use for the extension. 70 | ```json 71 | { 72 | "vsconan.conan.profile.configurations": { 73 | "my_conan1": { 74 | "conanVersion": "1", 75 | "conanExecutable": ".venv1/Scripts/conan.exe", 76 | "conanPythonInterpreter": ".venv1/Scripts/python.exe", 77 | "conanExecutionMode": "pythonInterpreter" 78 | }, 79 | "my_conan2": { 80 | "conanVersion": "2", 81 | "conanExecutable": ".venv2/Scripts/conan.exe", 82 | "conanPythonInterpreter": ".venv2/Scripts/python.exe", 83 | "conanExecutionMode": "pythonInterpreter", 84 | "conanUserHome": "/path/to/conan/home 85 | }, 86 | }, 87 | "vsconan.conan.profile.default": "my_conan1" 88 | } 89 | ``` 90 | * Overwrite conan home folder inside the profile with `conanUserHome` 91 | * Status bar to ease the switching between your predefined profiles 92 | * Status bar buttons for the following commands: 93 | * `vsconan.conan.install` 94 | * `vsconan.conan.build` 95 | * `vsconan.conan.create` 96 | * `vsconan.conan.buildenv` 97 | * `vsconan.conan.runenv` 98 | * `vsconan.conan.deactivateenv` 99 | -------------------------------------------------------------------------------- /docs/.pages: -------------------------------------------------------------------------------- 1 | nav: 2 | - index.md 3 | - Features 4 | - changelog.md -------------------------------------------------------------------------------- /docs/Features/index.md: -------------------------------------------------------------------------------- 1 | # Features -------------------------------------------------------------------------------- /docs/_assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/docs/_assets/favicon.png -------------------------------------------------------------------------------- /docs/_hooks/changelog.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | def on_pre_build(config): 5 | """Automatically copy the root CHANGELOG.md into docs/ before building and add a YAML header""" 6 | 7 | # Get all the necessary paths 8 | project_root = os.getcwd() # Most likely the project root 9 | docs_dir = os.path.join(project_root, "docs") # /docs/ 10 | 11 | # Define source and destination paths 12 | changelog_src = os.path.join(project_root, "CHANGELOG.md") # Root changelog path 13 | changelog_dst = os.path.join(docs_dir, "changelog.md") # Destination inside /docs/ 14 | 15 | yaml_header = """--- 16 | title: Change Log 17 | hide: 18 | - navigation 19 | --- 20 | """ 21 | 22 | # Ensure the source changelog exists before copying 23 | if os.path.exists(changelog_src): 24 | # Read the original changelog content 25 | with open(changelog_src, "r", encoding="utf-8") as src_file: 26 | changelog_content = src_file.read() 27 | 28 | # Write the new file with the header 29 | with open(changelog_dst, "w", encoding="utf-8") as dst_file: 30 | dst_file.write(yaml_header + "\n" + changelog_content) 31 | 32 | print(f"✅ Copied {changelog_src} to {changelog_dst} with YAML header") 33 | else: 34 | print(f"⚠️ WARNING: {changelog_src} not found. Skipping copy.") -------------------------------------------------------------------------------- /docs/_stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --md-primary-fg-color: #FF0000; 3 | --md-primary-fg-color-light: #ff0000; 4 | --md-primary-fg-color-dark: #A0B4C0; 5 | } -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Change Log 3 | hide: 4 | - navigation 5 | --- 6 | 7 | # Change Log 8 | 9 | ## 1.3.2 - 2025-02-11 10 | 11 | ### Fixed 12 | 13 | * [#49](https://github.com/afri-bit/vsconan/issues/49) Fix path issue with whitespaces 14 | 15 | ## 1.3.1 - 2024-10-22 16 | 17 | ### Fixed 18 | 19 | * Fix activation of BuildEnv/RunEnv 20 | 21 | ## 1.3.0 - 2024-09-10 22 | 23 | * [#36](https://github.com/afri-bit/vsconan/issues/36) Support the application of Conan's BuildEnv/RunEnv (Conan 2 only) 24 | * [#27](https://github.com/afri-bit/vsconan/issues/27) Added button in the status bar for frequently used conan commands 25 | 26 | ## 1.2.0 - 2024-08-03 27 | 28 | ### Added 29 | 30 | * [#39](https://github.com/afri-bit/vsconan/pull/39) Support detection of python interpreter by ms-python.python extension 31 | 32 | ## 1.1.0 - 2024-07-07 33 | 34 | ### Added 35 | 36 | * [#38](https://github.com/afri-bit/vsconan/issues/38) Support whitespace for project and configuration path 37 | You can now use whitespace in your configuration file and *VSConan* can still parse the path and use it for executing conan command. 38 | > Additional to this feature, internal the command builder is changed to separate the command and arguments. For further detail of the issue please refer to [#38](https://github.com/afri-bit/vsconan/issues/38). 39 | *This change should not affect the current configuration file.* 40 | 41 | Thanks to [torsknod-the-caridian](https://github.com/torsknod-the-caridian). 42 | 43 | ## 1.0.1 - 2024-02-04 44 | 45 | ### Changed 46 | 47 | * [#35](https://github.com/afri-bit/vsconan/issues/35) Extension cannot be activated after installation 48 | Due to missing dependencies or unability to find the dependencies, the extension cannot be started. So I replaced the small function that I used from this dependency with internal utility function. 49 | > Midnight programming mistake :P 50 | 51 | 52 | ## 1.0.0 - 2024-02-03 53 | 54 | ### Breaking Changes 55 | 56 | * Following settings in `settings.json` are **OBSOLETE** 57 | * `vsconan.general.pythonInterpreter` 58 | * `vsconan.general.conanExecutable` 59 | * `vsconan.general.conanExecutionMode` 60 | 61 | Instead, you can define multiple profiles according to your need in the `settings.json`. See example below: 62 | 63 | ```json 64 | "vsconan.conan.profile.configurations": { 65 | "myprofile_1": { 66 | "conanVersion": "1", 67 | "conanPythonInterpreter": "python", 68 | "conanExecutable": "conan", 69 | "conanExecutionMode": "pythonInterpreter", 70 | }, 71 | "myprofile_2": { 72 | "conanVersion": "2", 73 | "conanPythonInterpreter": "/home/user/.venv/bin/conan", 74 | "conanExecutable": "/home/user/.venv/bin/conan", 75 | "conanExecutionMode": "conanExecutable", 76 | "conanUserHome": "/home/user/your/path/to/.conan2" 77 | } 78 | }, 79 | "vsconan.conan.profile.default": "myprofile_2", 80 | ``` 81 | 82 | Using `vsconan.conan.profile.default` you can switch the profile easily, in case you have multiple conan setup or multiple python virtual environments with different conan versions. `conanUserHome` is optional parameter, in case you want to have a different location for `conan` home folder. 83 | 84 | * The workspace configuration to execute conan command, such as `build`, `install`, etc., is slightly changed, but it has a big impact to your workflow / usage of this configuration. 85 | > The `python` attribute is no longer available! 86 | 87 | Instead of using hard coded `python` path inside this json file, it will rely on the selected conan configuration profile. So with this change, you can use the same json file but using in the different conan version (Easy switch between conan 1 and 2). 88 | 89 | ```json 90 | { 91 | "commandContainer": { 92 | "create": [ 93 | { 94 | "name": "create", 95 | "description": "Create command", 96 | "detail": "Create command detail", 97 | "conanRecipe": "conanfile.py", 98 | "profile": "default", 99 | "user": "", 100 | "channel": "", 101 | "args": [] 102 | } 103 | ], 104 | "install": [ 105 | { 106 | "name": "install", 107 | "description": "Install command", 108 | "detail": "Install command detail", 109 | "conanRecipe": "conanfile.py", 110 | "installFolder": "install", 111 | "profile": "default", 112 | "user": "", 113 | "channel": "", 114 | "args": [] 115 | } 116 | ], 117 | . 118 | . 119 | . 120 | . 121 | } 122 | } 123 | ``` 124 | 125 | Due to the fact that there are some differences in the commands between conan 1 and 2, some attributes are added to some commands, that might not be used in one conan version or the other. 126 | Following example might give you clarity. 127 | 128 | * Conan 1 - `conan source` 129 | ```shell 130 | optional arguments: 131 | -h, --help 132 | -sf SOURCE_FOLDER, --source-folder SOURCE_FOLDER 133 | -if INSTALL_FOLDER, --install-folder INSTALL_FOLDER 134 | ``` 135 | 136 | * Conan 2 - `conan source` 137 | ```shell 138 | optional arguments: 139 | --name NAME 140 | --version VERSION 141 | --user USER 142 | --channel CHANNEL 143 | ``` 144 | 145 | ### Added 146 | * Conan 2 - Browsing the recipe with UI 147 | * Delete recipe 148 | * Open in VSCode 149 | * Open in explorer 150 | * Conan 2 - Browsing the packages with UI 151 | * Delete Package 152 | * Open in VSCode 153 | * Open in explorer 154 | * Conan 2 - Browsing the package revisions with UI 155 | * Delete package revision 156 | * Open in VSCode 157 | * Open in explorer 158 | * Conan 2 - Working with remotes with tree view 159 | * Same functionality as conan1 160 | * Conan 2 - Working with profiles with Tree view 161 | * Same functionality as conan1 162 | * Multiple profile definition for conan configuration in `settings.json` 163 | * Easy switch between conan configuration profile using status bar 164 | * Status bar view of selected conan configuration profile 165 | * Added new treeview for package revision (Only meant for conan2) 166 | 167 | ### Changed 168 | * New color palette for the VSConan logo :) Adapted the color according to the new official conan logo (roughly) 169 | 170 | ## 0.4.0 - 2022-09-11 171 | 172 | ### Added 173 | * [#24](https://github.com/afri-bit/vsconan/issues/24) Open different folders under recipe folder 174 | * User has the possibility to access different folders that are located under the recipe folder itself, such as `build`, `dl`, `source`, etc. 175 | * User can open the folder either in the explorer or in a new VS Code window. The option to open the folders can be found by using right click on the recipe item from the explorer. 176 | * [#17](https://github.com/afri-bit/vsconan/issues/17) Filter recipes based on a selected remote 177 | * [#18](https://github.com/afri-bit/vsconan/issues/18) Filter binary packages based on a selected remote 178 | 179 | ## 0.3.0 - 2022-06-06 180 | 181 | ### Added 182 | * [#16](https://github.com/afri-bit/vsconan/issues/16) Configuration in settings.json for `CONAN_USER_HOME` 183 | * Enable possibility to overwrite the pre defined `CONAN_USER_HOME` environment variable using `vsconan.general.conanUserHome` configuration option 184 | * User can set home directory to conan local cache within the VS Code using `settings.json` file 185 | 186 | ## 0.2.0 - 2022-05-30 187 | 188 | ### Added 189 | * [#10](https://github.com/afri-bit/vsconan/issues/10) Enable option to list dirty packages from a recipe 190 | * `vsconan.explorer.treeview.package.showDirtyPackage` is available to set the flag persistent 191 | * [#14](https://github.com/afri-bit/vsconan/issues/14) Support non-pip conan installation 192 | * Enable possibility for user to use the extension using alternative conan installation (e.g. conan executable) 193 | * Provide mode switch between python interpreter and conan executable (User can still use the python interpreter to execute conan CLI) 194 | * Configuration for the extension in `settings.json` 195 | * `vsconan.general.conanExecutable` 196 | * `vsconan.general.conanExecutionMode` 197 | * `vsconan.general.pythonInterpreter` 198 | * Right click option for recipe and package treeview item to copy its path 199 | * [#13](https://github.com/afri-bit/vsconan/issues/13) Managing editable packages 200 | * List editable packages in the treeview 201 | * Remove editable package via Treeview 202 | * Open editable package in VS Code 203 | * Open editable package in Explorer 204 | * Copy editable path to clipboard 205 | * Remove editable package via command and quickpick (simple option) 206 | * Add editable package from the workspace 207 | * Enable layout file input for the editable package 208 | **!!!** Currently only supporting the manual input from the user for the layout. 209 | 210 | 211 | ### Changed 212 | * The configuration for extension is migrated to official VS Code `settings.json`. Custom global `config.json` under `~/.vsconan` is now **deprecated**. 213 | 214 | ### Removed 215 | * `VSConan: Create Global Configuration (JSON)` 216 | Command to create global configuration file in your home directory 217 | * `VSConan: Open Global Configuration (JSON)` 218 | Open the global configuration file in editor 219 | 220 | ## 0.1.0 - 2022-04-21 221 | * Initial Release 222 | * Conan Explorer 223 | * Conan Recipe 224 | * Recipe information 225 | * Open in explorer 226 | * Open in VS Code 227 | * Remove recipe 228 | * Conan Binary Packages 229 | * Open in explorer 230 | * Open in VS Code 231 | * Remove binary package 232 | * Conan Profile 233 | * Add new profile 234 | * Edit profile 235 | * Open in explorer 236 | * Rename profile 237 | * Duplicate profile 238 | * Remove profile 239 | * Conan Remote 240 | * Edit `remotes.json` file in VS Code 241 | * Rename remote 242 | * Update remote URL 243 | * Enable remote 244 | * Disable remote 245 | * Remove remote 246 | * Conan Workspace 247 | * `conan create` 248 | * `conan install` 249 | * `conan build` 250 | * `conan source` 251 | * `conan package` 252 | * `conan export-pkg` 253 | * Additional Support Features 254 | * Create global configuration file 255 | * Open global configuration file 256 | * Create workspace configuration file 257 | * Open workspace configuration file -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Home 3 | --- 4 | 5 | # VSConan -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | roots: ['./test'], 4 | transform: { 5 | '^.+\\.ts$': ['ts-jest', { 6 | tsconfig: './tsconfig.json' 7 | }], 8 | }, 9 | testRegex: '\\.test\\.ts$', 10 | moduleFileExtensions: ['ts', 'js'], 11 | collectCoverageFrom: [ 12 | 'src/**/*.ts' 13 | ] 14 | }; -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: VSConan 2 | site_url: https://github.com/afri-bit/vsconan 3 | site_author: afri-bit 4 | site_description: >- 5 | Conan Extension for Visual Studio Code 6 | 7 | # Repository 8 | repo_name: vsconan 9 | repo_url: https://github.com/afri-bit/vsconan 10 | 11 | # Copyright 12 | copyright: Copyright © 2022 - present, afri-bit 13 | 14 | theme: 15 | name: material 16 | logo: _assets/favicon.png 17 | features: 18 | - announce.dismiss 19 | # - content.action.edit 20 | # - content.action.view 21 | - content.code.annotate 22 | - content.code.copy 23 | - content.code.select 24 | # - content.footnote.tooltips 25 | # - content.tabs.link 26 | - content.tooltips 27 | # - header.autohide 28 | - navigation.expand 29 | - navigation.footer 30 | - navigation.indexes 31 | # - navigation.instant 32 | # - navigation.instant.prefetch 33 | # - navigation.instant.progress 34 | - navigation.prune 35 | - navigation.sections 36 | - navigation.tabs 37 | - navigation.tabs.sticky 38 | - navigation.top 39 | - navigation.tracking 40 | - search.highlight 41 | - search.share 42 | - search.suggest 43 | - toc.follow 44 | # - toc.integrate 45 | palette: 46 | - media: "(prefers-color-scheme: light)" 47 | scheme: default 48 | primary: indigo 49 | accent: indigo 50 | toggle: 51 | icon: material/toggle-switch 52 | name: Switch to dark mode 53 | - media: "(prefers-color-scheme: dark)" 54 | scheme: slate 55 | primary: black 56 | accent: indigo 57 | toggle: 58 | icon: material/toggle-switch-off 59 | name: Switch to light mode 60 | font: 61 | text: Roboto 62 | code: Roboto Mono 63 | favicon: _assets/favicon.png 64 | icon: 65 | logo: logo 66 | 67 | markdown_extensions: 68 | - pymdownx.highlight: 69 | anchor_linenums: true 70 | line_spans: __span 71 | pygments_lang_class: true 72 | - pymdownx.inlinehilite 73 | - pymdownx.snippets 74 | - pymdownx.superfences: 75 | custom_fences: 76 | - name: mermaid 77 | class: mermaid 78 | format: !!python/name:pymdownx.superfences.fence_code_format 79 | - pymdownx.extra 80 | - attr_list 81 | - md_in_html 82 | - pymdownx.emoji: 83 | emoji_generator: !!python/name:material.extensions.emoji.to_svg 84 | emoji_index: !!python/name:material.extensions.emoji.twemoji 85 | - admonition 86 | - pymdownx.details 87 | - pymdownx.tabbed: 88 | alternate_style: true 89 | combine_header_slug: true 90 | slugify: !!python/object/apply:pymdownx.slugs.slugify 91 | kwds: 92 | case: lower 93 | - pymdownx.tasklist: 94 | custom_checkbox: true 95 | - abbr 96 | - def_list 97 | - footnotes 98 | 99 | plugins: 100 | - search 101 | - awesome-pages 102 | 103 | extra_css: 104 | - _stylesheets/extra.cs 105 | 106 | hooks: 107 | - docs/_hooks/changelog.py -------------------------------------------------------------------------------- /resources/icon/conan_io_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/conan_io_color.png -------------------------------------------------------------------------------- /resources/icon/conan_io_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/conan_io_gray.png -------------------------------------------------------------------------------- /resources/icon/dark/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/add.png -------------------------------------------------------------------------------- /resources/icon/dark/binary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/binary.png -------------------------------------------------------------------------------- /resources/icon/dark/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/copy.png -------------------------------------------------------------------------------- /resources/icon/dark/disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/disable.png -------------------------------------------------------------------------------- /resources/icon/dark/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/edit.png -------------------------------------------------------------------------------- /resources/icon/dark/enable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/enable.png -------------------------------------------------------------------------------- /resources/icon/dark/filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/filter.png -------------------------------------------------------------------------------- /resources/icon/dark/filter_clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/filter_clear.png -------------------------------------------------------------------------------- /resources/icon/dark/goto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/goto.png -------------------------------------------------------------------------------- /resources/icon/dark/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/info.png -------------------------------------------------------------------------------- /resources/icon/dark/off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/off.png -------------------------------------------------------------------------------- /resources/icon/dark/on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/on.png -------------------------------------------------------------------------------- /resources/icon/dark/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/profile.png -------------------------------------------------------------------------------- /resources/icon/dark/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/refresh.png -------------------------------------------------------------------------------- /resources/icon/dark/remote_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/remote_off.png -------------------------------------------------------------------------------- /resources/icon/dark/remote_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/remote_on.png -------------------------------------------------------------------------------- /resources/icon/dark/remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/remove.png -------------------------------------------------------------------------------- /resources/icon/dark/rename.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/rename.png -------------------------------------------------------------------------------- /resources/icon/dark/url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/url.png -------------------------------------------------------------------------------- /resources/icon/dark/vscode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/dark/vscode.png -------------------------------------------------------------------------------- /resources/icon/light/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/add.png -------------------------------------------------------------------------------- /resources/icon/light/binary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/binary.png -------------------------------------------------------------------------------- /resources/icon/light/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/copy.png -------------------------------------------------------------------------------- /resources/icon/light/disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/disable.png -------------------------------------------------------------------------------- /resources/icon/light/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/edit.png -------------------------------------------------------------------------------- /resources/icon/light/enable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/enable.png -------------------------------------------------------------------------------- /resources/icon/light/filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/filter.png -------------------------------------------------------------------------------- /resources/icon/light/filter_clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/filter_clear.png -------------------------------------------------------------------------------- /resources/icon/light/goto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/goto.png -------------------------------------------------------------------------------- /resources/icon/light/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/info.png -------------------------------------------------------------------------------- /resources/icon/light/off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/off.png -------------------------------------------------------------------------------- /resources/icon/light/on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/on.png -------------------------------------------------------------------------------- /resources/icon/light/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/profile.png -------------------------------------------------------------------------------- /resources/icon/light/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/refresh.png -------------------------------------------------------------------------------- /resources/icon/light/remote_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/remote_off.png -------------------------------------------------------------------------------- /resources/icon/light/remote_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/remote_on.png -------------------------------------------------------------------------------- /resources/icon/light/remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/remove.png -------------------------------------------------------------------------------- /resources/icon/light/rename.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/rename.png -------------------------------------------------------------------------------- /resources/icon/light/url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/url.png -------------------------------------------------------------------------------- /resources/icon/light/vscode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/light/vscode.png -------------------------------------------------------------------------------- /resources/icon/package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/package.png -------------------------------------------------------------------------------- /resources/icon/package_dirty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/package_dirty.png -------------------------------------------------------------------------------- /resources/icon/package_revision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/package_revision.png -------------------------------------------------------------------------------- /resources/icon/recipe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/recipe.png -------------------------------------------------------------------------------- /resources/icon/recipe_editable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/recipe_editable.png -------------------------------------------------------------------------------- /resources/icon/vsconan-explorer-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/vsconan-explorer-logo.png -------------------------------------------------------------------------------- /resources/icon/vsconan-extension-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/vsconan-extension-logo.png -------------------------------------------------------------------------------- /resources/icon/vsconan-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/icon/vsconan-logo.png -------------------------------------------------------------------------------- /resources/img/conan_package_treeview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/img/conan_package_treeview.png -------------------------------------------------------------------------------- /resources/img/conan_profile_treeview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/img/conan_profile_treeview.png -------------------------------------------------------------------------------- /resources/img/conan_recipe_treeview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/img/conan_recipe_treeview.png -------------------------------------------------------------------------------- /resources/img/conan_remote_treeview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/img/conan_remote_treeview.png -------------------------------------------------------------------------------- /resources/img/demo_explorer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/img/demo_explorer.gif -------------------------------------------------------------------------------- /resources/img/demo_workspace.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/img/demo_workspace.gif -------------------------------------------------------------------------------- /resources/img/prompt_conan_project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/img/prompt_conan_project.png -------------------------------------------------------------------------------- /resources/img/vsconan_gui_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afri-bit/vsconan/3d610e38406ceda69c44c4e69160c32ae001ebd8/resources/img/vsconan_gui_settings.png -------------------------------------------------------------------------------- /resources/print_env.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import sys 4 | 5 | from conan.api import conan_api 6 | from conan.api.output import ConanOutput 7 | from conan.cli.args import common_graph_args, validate_common_graph_args 8 | from conan.errors import ConanException 9 | from conan.tools.env import VirtualBuildEnv, VirtualRunEnv 10 | 11 | 12 | def print_env(conan_api, whichenv, args): 13 | """ 14 | Print requested environment. 15 | 16 | More or less a copy of https://github.com/conan-io/conan/blob/917ce14b5e4d9e9c7bb78c47fc0ba785f690f8ac/conan/cli/commands/install.py#L43 17 | """ 18 | # basic paths 19 | cwd = os.getcwd() 20 | path = ( 21 | conan_api.local.get_conanfile_path(args.path, cwd, py=None) 22 | if args.path 23 | else None 24 | ) 25 | 26 | # Basic collaborators: remotes, lockfile, profiles 27 | remotes = conan_api.remotes.list(args.remote) if not args.no_remote else [] 28 | overrides = eval(args.lockfile_overrides) if args.lockfile_overrides else None 29 | lockfile = conan_api.lockfile.get_lockfile( 30 | lockfile=args.lockfile, 31 | conanfile_path=path, 32 | cwd=cwd, 33 | partial=args.lockfile_partial, 34 | overrides=overrides, 35 | ) 36 | profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args) 37 | 38 | # Graph computation (without installation of binaries) 39 | gapi = conan_api.graph 40 | deps_graph = gapi.load_graph_consumer( 41 | path, 42 | args.name, 43 | args.version, 44 | args.user, 45 | args.channel, 46 | profile_host, 47 | profile_build, 48 | lockfile, 49 | remotes, 50 | args.update, 51 | # is_build_require=args.build_require, 52 | ) 53 | gapi.analyze_binaries( 54 | deps_graph, args.build, remotes, update=args.update, lockfile=lockfile 55 | ) 56 | # print_graph_packages(deps_graph) 57 | conan_api.install.install_binaries(deps_graph=deps_graph, remotes=remotes) 58 | 59 | conanfile = deps_graph.root.conanfile 60 | 61 | if whichenv == "BuildEnv": 62 | env = VirtualBuildEnv(conanfile) 63 | vars = env.vars(scope="build") 64 | else: 65 | env = VirtualRunEnv(conanfile) 66 | vars = env.vars(scope="run") 67 | return dict(vars.items()) 68 | 69 | 70 | if __name__ == "__main__": 71 | parser = argparse.ArgumentParser() 72 | parser.add_argument("whichenv", choices=("BuildEnv", "RunEnv")) 73 | common_graph_args(parser) 74 | args = parser.parse_args() 75 | validate_common_graph_args(args) 76 | if not args.path: 77 | raise ConanException("Please specify a path to a conanfile") 78 | args.no_remote = True 79 | 80 | ConanOutput.define_log_level("quiet") 81 | env = print_env(conan_api.ConanAPI(), args.whichenv, args) 82 | env["PATH"] = os.pathsep.join( 83 | (os.path.dirname(sys.executable), env.get("PATH", os.environ["PATH"])) 84 | ) 85 | 86 | import json 87 | 88 | print(json.dumps(env)) 89 | -------------------------------------------------------------------------------- /src/conans/api/conanAPIManager.ts: -------------------------------------------------------------------------------- 1 | import { Conan1API } from "../conan/api/conanAPI"; 2 | import { Conan2API } from "../conan2/api/conanAPI"; 3 | import { ConanAPI, ConanExecutionMode } from "./base/conanAPI"; 4 | 5 | export class ConanAPIManager { 6 | private _conanVersion: string = ""; 7 | private _conanApi: ConanAPI | undefined = undefined; 8 | 9 | public constructor(conanVersion: string = "", 10 | pythonInterpreter: string = "", 11 | conanExecutable: string = "", 12 | conanExecutionMode: ConanExecutionMode = ConanExecutionMode.python) { 13 | this.setApiInstance(conanVersion, pythonInterpreter, conanExecutable, conanExecutionMode); 14 | } 15 | 16 | public setApiInstance(conanVersion: string, pythonInterpreter: string, conanExecutable: string, conanExecutionMode: ConanExecutionMode) { 17 | if (conanVersion === "1") { 18 | this._conanApi = new Conan1API(pythonInterpreter, conanExecutable, conanExecutionMode); 19 | this._conanVersion = conanVersion; 20 | } 21 | else if (conanVersion === "2") { 22 | this._conanApi = new Conan2API(pythonInterpreter, conanExecutable, conanExecutionMode); 23 | this._conanVersion = conanVersion; 24 | } 25 | else { 26 | this._conanApi = undefined; 27 | this._conanVersion = ""; 28 | } 29 | } 30 | 31 | public get conanVersion(): string { 32 | return this._conanVersion; 33 | } 34 | 35 | public get conanApi(): ConanAPI { 36 | return this._conanApi!; 37 | } 38 | 39 | public switchExecutionMode(mode: ConanExecutionMode): void { 40 | this.conanApi?.switchExecutionMode(mode); 41 | } 42 | 43 | public setPythonInterpreter(pythonInterpreter: string): void { 44 | this.conanApi?.setPythonInterpreter(pythonInterpreter); 45 | } 46 | 47 | public switchToPythonMode(pythonInterpreter: string): void { 48 | this.conanApi?.switchToPythonMode(pythonInterpreter); 49 | } 50 | 51 | public setConanExecutable(conanExecutable: string): void { 52 | this.conanApi?.setConanExecutable(conanExecutable); 53 | } 54 | 55 | public switchToConanExecutableMode(conanExecutable: string): void { 56 | this.conanApi?.switchToConanExecutableMode(conanExecutable); 57 | } 58 | } -------------------------------------------------------------------------------- /src/conans/command/commandBuilder.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ConfigCommandBuild, 3 | ConfigCommandCreate, 4 | ConfigCommandInstall, 5 | ConfigCommandPackage, 6 | ConfigCommandPackageExport, 7 | ConfigCommandSource 8 | } from "./configCommand"; 9 | 10 | /** 11 | * Static class to build command for some conan workflow based on the configuration. 12 | */ 13 | export abstract class CommandBuilder { 14 | /** 15 | * Build command for 'conan create' 16 | * @param wsPath Absolute path of conan workspace. This is needed since user can put a relative path, which means relative path to the workspace. 17 | * @param cfg Command configuration 18 | * @returns Full CLI command for 'conan create' | undefined on error 19 | */ 20 | public abstract buildCommandCreate(wsPath: string, cfg: ConfigCommandCreate): Array<string> | undefined; 21 | 22 | /** 23 | * Build command for 'conan install' 24 | * @param wsPath Absolute path of conan workspace. This is needed since user can put a relative path, which means relative path to the workspace. 25 | * @param cfg Command configuration 26 | * @returns Full CLI command for 'conan install' | undefined on error 27 | */ 28 | public abstract buildCommandInstall(wsPath: string, cfg: ConfigCommandInstall): Array<string> | undefined; 29 | 30 | /** 31 | * Build command for 'conan build' 32 | * @param wsPath Absolute path of conan workspace. This is needed since user can put a relative path, which means relative path to the workspace. 33 | * @param cfg Command configuration 34 | * @returns Full CLI command for 'conan build' | undefined on error 35 | */ 36 | public abstract buildCommandBuild(wsPath: string, cfg: ConfigCommandBuild): Array<string> | undefined; 37 | 38 | /** 39 | * Build command for 'conan source' 40 | * @param wsPath Absolute path of conan workspace. This is needed since user can put a relative path, which means relative path to the workspace. 41 | * @param cfg Command configuration 42 | * @returns Full CLI command for 'conan source' | undefined on error 43 | */ 44 | public abstract buildCommandSource(wsPath: string, cfg: ConfigCommandSource): Array<string> | undefined; 45 | 46 | /** 47 | * Build command for 'conan package' 48 | * @param wsPath Absolute path of conan workspace. This is needed since user can put a relative path, which means relative path to the workspace. 49 | * @param python Path to python interpreter, where conan is installed 50 | * @param cfg Command configuration 51 | * @returns Full CLI command for 'conan package' | undefined on error 52 | */ 53 | public abstract buildCommandPackage(wsPath: string, cfg: ConfigCommandPackage): Array<string> | undefined; 54 | 55 | /** 56 | * Build command for 'conan export-pkg' 57 | * @param wsPath Absolute path of conan workspace. This is needed since user can put a relative path, which means relative path to the workspace. 58 | * @param python Path to python interpreter, where conan is installed 59 | * @param cfg Command configuration 60 | * @returns Full CLI command for 'conan export-pkg' | undefined on error 61 | */ 62 | public abstract buildCommandPackageExport(wsPath: string, cfg: ConfigCommandPackageExport): Array<string> | undefined; 63 | } -------------------------------------------------------------------------------- /src/conans/command/commandBuilderFactory.ts: -------------------------------------------------------------------------------- 1 | import { CommandBuilderConan1 } from "../conan/commandBuilder"; 2 | import { CommandBuilderConan2 } from "../conan2/commandBuilder"; 3 | import { CommandBuilder } from "./commandBuilder"; 4 | 5 | export class CommandBuilderFactory { 6 | 7 | public static getCommandBuilder(conanVersion: string): CommandBuilder | undefined { 8 | if (conanVersion === "1") { 9 | return new CommandBuilderConan1(); 10 | } 11 | else if (conanVersion === "2") { 12 | return new CommandBuilderConan2(); 13 | } 14 | else { 15 | return undefined; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/conans/command/configCommand.ts: -------------------------------------------------------------------------------- 1 | 2 | export class ConfigCommand { 3 | public name: string; 4 | public description: string; 5 | public detail: string; 6 | public conanRecipe: string; 7 | 8 | constructor(name: string = "", description: string = "", detail: string = "", conanRecipe = "conanfile.py") { 9 | this.name = name; 10 | this.description = description; 11 | this.detail = detail; 12 | this.conanRecipe = conanRecipe; 13 | } 14 | } 15 | 16 | export class ConfigCommandCreate extends ConfigCommand { 17 | public profile: string; 18 | public user: string; 19 | public channel: string; 20 | public args: Array<string>; 21 | 22 | constructor(name: string = "create", 23 | description: string = "Create command", 24 | detail: string = "Create command detail", 25 | profile: string = "default", 26 | user: string = "", 27 | channel: string = "", 28 | args: Array<string> = []) { 29 | 30 | super(name, description, detail); 31 | this.profile = profile; 32 | this.user = user; 33 | this.channel = channel; 34 | this.args = args; 35 | } 36 | } 37 | 38 | export class ConfigCommandInstall extends ConfigCommand { 39 | public installFolder: string; 40 | public profile: string; 41 | public user: string; 42 | public channel: string; 43 | public args: Array<string>; 44 | 45 | constructor(name: string = "install", 46 | description: string = "Install command", 47 | detail: string = "Install command detail", 48 | installFolder: string = "install", 49 | profile: string = "default", 50 | user: string = "", 51 | channel: string = "", 52 | args: Array<string> = []) { 53 | super(name, description, detail); 54 | this.installFolder = installFolder; 55 | this.profile = profile; 56 | this.user = user; 57 | this.channel = channel; 58 | this.args = args; 59 | } 60 | } 61 | 62 | export class ConfigCommandBuild extends ConfigCommand { 63 | public installFolder: string; 64 | public buildFolder: string; 65 | public packageFolder: string; 66 | public sourceFolder: string; 67 | public args: Array<string>; 68 | 69 | constructor(name: string = "build", 70 | description: string = "Build command", 71 | detail: string = "Build command detail", 72 | installFolder: string = "install", 73 | buildFolder: string = "build", 74 | packageFolder: string = "package", 75 | sourceFolder: string = "source", 76 | args: Array<string> = []) { 77 | super(name, description, detail); 78 | this.installFolder = installFolder; 79 | this.buildFolder = buildFolder; 80 | this.packageFolder = packageFolder; 81 | this.sourceFolder = sourceFolder; 82 | this.args = args; 83 | } 84 | } 85 | 86 | export class ConfigCommandSource extends ConfigCommand { 87 | public installFolder: string; 88 | public sourceFolder: string; 89 | public version: string; 90 | public user: string; 91 | public channel: string; 92 | public args: Array<string>; 93 | 94 | constructor(name: string = "source", 95 | description: string = "Source command", 96 | detail: string = "Source command detail", 97 | installFolder: string = "install", 98 | sourceFolder: string = "source", 99 | version: string = "", 100 | user: string = "", 101 | channel: string = "", 102 | args: Array<string> = []) { 103 | super(name, description, detail); 104 | this.installFolder = installFolder; 105 | this.sourceFolder = sourceFolder; 106 | this.version = version; 107 | this.user = user; 108 | this.channel = channel; 109 | this.args = args; 110 | } 111 | } 112 | 113 | export class ConfigCommandPackage extends ConfigCommand { 114 | public installFolder: string; 115 | public buildFolder: string; 116 | public packageFolder: string; 117 | public sourceFolder: string; 118 | 119 | constructor(name: string = "pkg", 120 | description: string = "Package command", 121 | detail: string = "Package command detail", 122 | installFolder: string = "install", 123 | buildFolder: string = "build", 124 | packageFolder: string = "package", 125 | sourceFolder: string = "source") { 126 | super(name, description, detail); 127 | this.installFolder = installFolder; 128 | this.buildFolder = buildFolder; 129 | this.packageFolder = packageFolder; 130 | this.sourceFolder = sourceFolder; 131 | } 132 | } 133 | 134 | export class ConfigCommandPackageExport extends ConfigCommand { 135 | public installFolder: string; 136 | public buildFolder: string; 137 | public packageFolder: string; 138 | public sourceFolder: string; 139 | public user: string; 140 | public channel: string; 141 | public args: Array<string>; 142 | 143 | constructor(name: string = "pkg_export", 144 | description: string = "Package export command", 145 | detail: string = "Package export command detail", 146 | installFolder: string = "install", 147 | buildFolder: string = "build", 148 | packageFolder: string = "package", 149 | sourceFolder: string = "source", 150 | user: string = "", 151 | channel: string = "", 152 | args: Array<string> = []) { 153 | super(name, description, detail); 154 | this.installFolder = installFolder; 155 | this.buildFolder = buildFolder; 156 | this.packageFolder = packageFolder; 157 | this.sourceFolder = sourceFolder; 158 | this.user = user; 159 | this.channel = channel; 160 | this.args = args; 161 | } 162 | } 163 | 164 | export class CommandContainer { 165 | public create: Array<ConfigCommandCreate>; 166 | public install: Array<ConfigCommandInstall>; 167 | public build: Array<ConfigCommandBuild>; 168 | public source: Array<ConfigCommandSource>; 169 | public pkg: Array<ConfigCommandPackage>; 170 | public pkgExport: Array<ConfigCommandPackageExport>; 171 | 172 | constructor(create: Array<ConfigCommandCreate> = [], 173 | install: Array<ConfigCommandInstall> = [], 174 | build: Array<ConfigCommandBuild> = [], 175 | source: Array<ConfigCommandSource> = [], 176 | pkg: Array<ConfigCommandPackage> = [], 177 | pkgExport: Array<ConfigCommandPackageExport> = []) { 178 | 179 | this.create = create; 180 | this.install = install; 181 | this.build = build; 182 | this.source = source; 183 | this.pkg = pkg; 184 | this.pkgExport = pkgExport; 185 | } 186 | } -------------------------------------------------------------------------------- /src/conans/conan/commandBuilder.ts: -------------------------------------------------------------------------------- 1 | import * as utils from "../../utils/utils"; 2 | import { CommandBuilder } from "../command/commandBuilder"; 3 | import { 4 | ConfigCommandBuild, 5 | ConfigCommandCreate, 6 | ConfigCommandInstall, 7 | ConfigCommandPackage, 8 | ConfigCommandPackageExport, 9 | ConfigCommandSource 10 | } from "../command/configCommand"; 11 | 12 | /** 13 | * Static class to build command for some conan workflow based on the configuration. 14 | */ 15 | export class CommandBuilderConan1 extends CommandBuilder { 16 | 17 | public override buildCommandCreate(wsPath: string, cfg: ConfigCommandCreate): Array<string> | undefined { 18 | // Initialized the command in array of string. Later on will be converted to plain string. 19 | let cmd: Array<string> = []; 20 | 21 | // One of mandatory attributes is the path to the conanfile.py 22 | // If this is empty the whole command build process will be cancelled. 23 | if (cfg.conanRecipe !== "" && cfg.conanRecipe !== undefined) { 24 | cmd.push(utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.conanRecipe)); 25 | } 26 | else { 27 | return undefined; 28 | } 29 | 30 | // Check if user and channel are specified 31 | if ((cfg.user !== undefined && cfg.user !== "") && (cfg.channel !== undefined && cfg.channel !== "")) { 32 | cmd.push(cfg.user + "/" + cfg.channel); 33 | } 34 | 35 | // Check if there is a specific profile define for this command 36 | if (cfg.profile !== "" && cfg.profile !== undefined) { 37 | cmd.push.apply(cmd, ["-pr", cfg.profile]); 38 | } 39 | 40 | // Push additional arguments that user can define 41 | cmd.push.apply(cmd, cfg.args); 42 | 43 | return cmd; 44 | } 45 | 46 | public override buildCommandInstall(wsPath: string, cfg: ConfigCommandInstall): Array<string> | undefined { 47 | let cmd: Array<string> = []; 48 | 49 | // One of mandatory attributes is the path to the conanfile.py 50 | // If this is empty the whole command build process will be cancelled. 51 | if (cfg.conanRecipe !== "" && cfg.conanRecipe !== undefined) { 52 | cmd.push(utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.conanRecipe)); 53 | } 54 | else { 55 | return undefined; 56 | } 57 | 58 | if ((cfg.user !== undefined && cfg.user !== "") && (cfg.channel !== undefined && cfg.channel !== "")) { 59 | cmd.push(cfg.user + "/" + cfg.channel); 60 | } 61 | 62 | if (cfg.profile !== "" && cfg.profile !== undefined) { 63 | cmd.push.apply(cmd, ["-pr", cfg.profile]); 64 | } 65 | 66 | if (cfg.installFolder !== "" && cfg.installFolder !== undefined) { 67 | cmd.push.apply(cmd, ["-if", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.installFolder)]); 68 | } 69 | 70 | cmd.push.apply(cmd, cfg.args); 71 | 72 | return cmd; // TODO: Clean up - Returning the array of string 73 | 74 | } 75 | 76 | public override buildCommandBuild(wsPath: string, cfg: ConfigCommandBuild): Array<string> | undefined { 77 | let cmd: Array<string> = []; 78 | 79 | if (cfg.conanRecipe !== "" && cfg.conanRecipe !== undefined) { 80 | cmd.push(utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.conanRecipe)); 81 | } 82 | else { 83 | return undefined; 84 | } 85 | 86 | if (cfg.installFolder !== "" && cfg.installFolder !== undefined) { 87 | cmd.push.apply(cmd, ["-if", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.installFolder)]); 88 | } 89 | 90 | if (cfg.buildFolder !== "" && cfg.buildFolder !== undefined) { 91 | cmd.push.apply(cmd, ["-bf", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.buildFolder)]); 92 | } 93 | 94 | if (cfg.packageFolder !== "" && cfg.packageFolder !== undefined) { 95 | cmd.push.apply(cmd, ["-pf", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.packageFolder)]); 96 | } 97 | 98 | if (cfg.sourceFolder !== "" && cfg.sourceFolder !== undefined) { 99 | cmd.push.apply(cmd, ["-sf", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.sourceFolder)]); 100 | } 101 | 102 | cmd.push.apply(cmd, cfg.args); 103 | 104 | return cmd; 105 | 106 | } 107 | 108 | public override buildCommandSource(wsPath: string, cfg: ConfigCommandSource): Array<string> | undefined { 109 | 110 | let cmd: Array<string> = []; 111 | 112 | // cmd.push("source"); 113 | 114 | if (cfg.conanRecipe !== "" && cfg.conanRecipe !== undefined) { 115 | cmd.push(utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.conanRecipe)); 116 | } 117 | else { 118 | return undefined; 119 | } 120 | 121 | if (cfg.installFolder !== "" && cfg.installFolder !== undefined) { 122 | cmd.push.apply(cmd, ["-if", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.installFolder)]); 123 | } 124 | 125 | if (cfg.sourceFolder !== "" && cfg.sourceFolder !== undefined) { 126 | cmd.push.apply(cmd, ["-sf", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.sourceFolder)]); 127 | } 128 | 129 | return cmd; 130 | 131 | } 132 | 133 | public override buildCommandPackage(wsPath: string, cfg: ConfigCommandPackage): Array<string> | undefined { 134 | let cmd: Array<string> = []; 135 | 136 | if (cfg.conanRecipe !== "" && cfg.conanRecipe !== undefined) { 137 | cmd.push(utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.conanRecipe)); 138 | } 139 | else { 140 | return undefined; 141 | } 142 | 143 | if (cfg.installFolder !== "" && cfg.installFolder !== undefined) { 144 | cmd.push.apply(cmd, ["-if", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.installFolder)]); 145 | } 146 | 147 | if (cfg.buildFolder !== "" && cfg.buildFolder !== undefined) { 148 | cmd.push.apply(cmd, ["-bf", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.buildFolder)]); 149 | } 150 | 151 | if (cfg.packageFolder !== "" && cfg.packageFolder !== undefined) { 152 | cmd.push.apply(cmd, ["-pf", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.packageFolder)]); 153 | } 154 | 155 | if (cfg.sourceFolder !== "" && cfg.sourceFolder !== undefined) { 156 | cmd.push.apply(cmd, ["-sf", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.sourceFolder)]); 157 | } 158 | 159 | return cmd; 160 | 161 | } 162 | 163 | public override buildCommandPackageExport(wsPath: string, cfg: ConfigCommandPackageExport): Array<string> | undefined { 164 | let cmd: Array<string> = []; 165 | 166 | if (cfg.conanRecipe !== "" && cfg.conanRecipe !== undefined) { 167 | cmd.push(utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.conanRecipe)); 168 | } 169 | else { 170 | return undefined; 171 | } 172 | 173 | if (cfg.installFolder !== "" && cfg.installFolder !== undefined) { 174 | cmd.push.apply(cmd, ["-if", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.installFolder)]); 175 | } 176 | 177 | if (cfg.buildFolder !== "" && cfg.buildFolder !== undefined) { 178 | cmd.push.apply(cmd, ["-bf", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.buildFolder)]); 179 | } 180 | 181 | if (cfg.packageFolder !== "" && cfg.packageFolder !== undefined) { 182 | cmd.push.apply(cmd, ["-pf", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.packageFolder)]); 183 | } 184 | 185 | if (cfg.sourceFolder !== "" && cfg.sourceFolder !== undefined) { 186 | cmd.push.apply(cmd, ["-sf", utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.sourceFolder)]); 187 | } 188 | 189 | cmd.push.apply(cmd, cfg.args); 190 | 191 | return cmd; 192 | } 193 | } -------------------------------------------------------------------------------- /src/conans/conan2/commandBuilder.ts: -------------------------------------------------------------------------------- 1 | import * as utils from "../../utils/utils"; 2 | import { CommandBuilder } from "../command/commandBuilder"; 3 | import { 4 | ConfigCommandBuild, 5 | ConfigCommandCreate, 6 | ConfigCommandInstall, 7 | ConfigCommandPackage, 8 | ConfigCommandPackageExport, 9 | ConfigCommandSource 10 | } from "../command/configCommand"; 11 | 12 | /** 13 | * Static class to build command for some conan workflow based on the configuration. 14 | */ 15 | export class CommandBuilderConan2 extends CommandBuilder { 16 | 17 | public override buildCommandCreate(wsPath: string, cfg: ConfigCommandCreate): Array<string> | undefined { 18 | // Initialized the command in array of string. Later on will be converted to plain string. 19 | let cmd: Array<string> = []; 20 | 21 | if (cfg.conanRecipe) { 22 | cmd.push(utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.conanRecipe)); 23 | } 24 | else { 25 | return undefined; 26 | } 27 | 28 | if (cfg.user) { cmd.push.apply(cmd, ["--user", cfg.user]); } 29 | 30 | if (cfg.channel) { cmd.push.apply(cmd, ["--channel", cfg.channel]); } 31 | 32 | if (cfg.profile) { cmd.push.apply(cmd, ["-pr", cfg.profile]); } 33 | 34 | // Push additional arguments that user can define 35 | cmd.push.apply(cmd, cfg.args); 36 | 37 | return cmd; 38 | 39 | } 40 | 41 | public override buildCommandInstall(wsPath: string, cfg: ConfigCommandInstall): Array<string> | undefined { 42 | let cmd: Array<string> = []; 43 | 44 | if (cfg.conanRecipe) { 45 | cmd.push(utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.conanRecipe)); 46 | } 47 | else { 48 | return undefined; 49 | } 50 | 51 | if (cfg.user) { cmd.push.apply(cmd, ["--user", cfg.user]); } 52 | 53 | if (cfg.channel) { cmd.push.apply(cmd, ["--channel", cfg.channel]); } 54 | 55 | if (cfg.profile) { cmd.push.apply(cmd, ["-pr", cfg.profile]); } 56 | 57 | // NOTE: Install folder argument is ignored in conan2, conan will generate build folder automatically 58 | 59 | cmd.push.apply(cmd, cfg.args); 60 | 61 | return cmd; 62 | } 63 | 64 | public override buildCommandBuild(wsPath: string, cfg: ConfigCommandBuild): Array<string> | undefined { 65 | let cmd: Array<string> = []; 66 | 67 | if (cfg.conanRecipe) { 68 | cmd.push(utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.conanRecipe)); 69 | } 70 | else { 71 | return undefined; 72 | } 73 | 74 | // NOTE: Ignore following input for now: (only works for conan 1) 75 | // install Folder 76 | // build folder 77 | // package folder 78 | // source folder 79 | 80 | cmd.push.apply(cmd, cfg.args); 81 | 82 | return cmd; 83 | 84 | } 85 | 86 | public override buildCommandSource(wsPath: string, cfg: ConfigCommandSource): Array<string> | undefined { 87 | 88 | let cmd: Array<string> = []; 89 | 90 | if (cfg.conanRecipe) { 91 | cmd.push(utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.conanRecipe)); 92 | } 93 | else { 94 | return undefined; 95 | } 96 | 97 | if (cfg.user) { cmd.push.apply(cmd, ["--user", cfg.user]); } 98 | 99 | if (cfg.channel) { cmd.push.apply(cmd, ["--channel", cfg.channel]); } 100 | 101 | // NOTE: Ignoring following input for now (only works for conan 1) 102 | // Install Folder 103 | // Source Folder 104 | 105 | cmd.push.apply(cmd, cfg.args); 106 | 107 | return cmd; 108 | 109 | } 110 | 111 | public override buildCommandPackage(wsPath: string, cfg: ConfigCommandPackage): Array<string> | undefined { 112 | // No 'package' command in conan 2 113 | return undefined; 114 | } 115 | 116 | public override buildCommandPackageExport(wsPath: string, cfg: ConfigCommandPackageExport): Array<string> | undefined { 117 | let cmd: Array<string> = []; 118 | 119 | if (cfg.conanRecipe) { 120 | cmd.push(utils.workspace.getAbsolutePathFromWorkspace(wsPath, cfg.conanRecipe)); 121 | } 122 | else { 123 | return undefined; 124 | } 125 | 126 | if (cfg.user) { cmd.push.apply(cmd, ["--user", cfg.user]); } 127 | 128 | if (cfg.channel) { cmd.push.apply(cmd, ["--channel", cfg.channel]); } 129 | 130 | cmd.push.apply(cmd, cfg.args); 131 | 132 | return cmd; 133 | 134 | } 135 | } -------------------------------------------------------------------------------- /src/conans/model/conanPackage.ts: -------------------------------------------------------------------------------- 1 | 2 | export class ConanPackage { 3 | public id: string; 4 | public dirty: boolean; 5 | public options: object; 6 | public outdated: boolean; 7 | public requires: object; 8 | public settings: object; 9 | public path: string; 10 | 11 | constructor(id: string, dirty: boolean, options: object, outdated: boolean, requires: object, settings: object, path: string = "") { 12 | this.id = id; 13 | this.dirty = dirty; 14 | this.options = options; 15 | this.outdated = outdated; 16 | this.requires = requires; 17 | this.settings = settings; 18 | this.path = path; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/conans/model/conanPackageRevision.ts: -------------------------------------------------------------------------------- 1 | 2 | export class ConanPackageRevision { 3 | public id: string; 4 | public timestamp: number; 5 | public datetime: string; 6 | 7 | constructor(id: string, timestamp: number) { 8 | this.id = id; 9 | this.timestamp = timestamp; 10 | 11 | let date = new Date(this.timestamp * 1000); 12 | this.datetime = date.toLocaleString(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/conans/model/conanProfile.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export class ConanProfile { 4 | public name: string; 5 | public data: object; // TODO: Fix this if the object can be specified, this should be detail information about the profile itself, such as compiler etc. 6 | 7 | constructor(name: string, data: object) { 8 | this.name = name; 9 | this.data = data; 10 | } 11 | } -------------------------------------------------------------------------------- /src/conans/model/conanRecipe.ts: -------------------------------------------------------------------------------- 1 | import { ConanPackage } from "./conanPackage"; 2 | 3 | export class ConanRecipe { 4 | public name: string; 5 | public editable: boolean; 6 | public path: string; 7 | public binaryPackages: Map<string, ConanPackage>; 8 | 9 | constructor(name: string, editable: boolean, path: string = "", binaryPackages: Map<string, ConanPackage> = new Map<string, ConanPackage>()) { 10 | this.name = name; 11 | this.editable = editable; 12 | this.path = path; 13 | this.binaryPackages = binaryPackages; 14 | } 15 | } -------------------------------------------------------------------------------- /src/conans/model/conanRemote.ts: -------------------------------------------------------------------------------- 1 | 2 | export class ConanRemote { 3 | public name: string; 4 | public url: string; 5 | public verifySsl: boolean; 6 | public enabled: boolean; 7 | public listRef: Set<string> = new Set<string>(); 8 | 9 | constructor(name: string, url: string, verifySsl: boolean, enabled: boolean) { 10 | this.name = name; 11 | this.url = url; 12 | this.verifySsl = verifySsl; 13 | this.enabled = enabled; 14 | } 15 | 16 | public rename(newName: string) { 17 | this.name = newName; 18 | } 19 | 20 | public updateUrl(newUrl: string) { 21 | this.url = newUrl; 22 | } 23 | 24 | public addRef(ref: string) { 25 | this.listRef.add(ref); 26 | } 27 | } -------------------------------------------------------------------------------- /src/conans/workspace/configWorkspace.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import { CommandContainer } from "../command/configCommand"; 3 | 4 | export class ConfigWorkspace { 5 | public commandContainer: CommandContainer; 6 | 7 | constructor(commandContainer: CommandContainer = new CommandContainer()) { 8 | this.commandContainer = commandContainer; 9 | } 10 | 11 | public getJsonString(): string { 12 | let jsonString = JSON.stringify(this, null, 4); 13 | return jsonString; 14 | } 15 | 16 | /** 17 | * Save current configuration to JSON file with give file name 18 | * 19 | * @param filename 20 | */ 21 | public writeToFile(filename: string) { 22 | let jsonString = JSON.stringify(this, null, 4); 23 | fs.writeFile(filename, jsonString, "utf8", function (err) { 24 | if (err) { 25 | throw err; 26 | } 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import * as path from "path"; 3 | import * as vscode from "vscode"; 4 | import * as constants from "./utils/constants"; 5 | import * as utils from "./utils/utils"; 6 | 7 | import { ConanAPIManager } from "./conans/api/conanAPIManager"; 8 | import { ConanCacheExplorerManager } from "./extension/manager/explorer/conanCache"; 9 | import { ConanProfileExplorerManager } from "./extension/manager/explorer/conanProfile"; 10 | import { ConanRemoteExplorerManager } from "./extension/manager/explorer/conanRemote"; 11 | import { VSConanWorkspaceManager } from "./extension/manager/vsconanWorkspace"; 12 | import { SettingsManager } from "./extension/settings/settingsManager"; 13 | import { SettingsPropertyManager } from "./extension/settings/settingsPropertyManager"; 14 | import { ConanPackageNodeProvider } from "./extension/ui/treeview/conanPackageProvider"; 15 | import { ConanPackageRevisionNodeProvider } from "./extension/ui/treeview/conanPackageRevisionProvider"; 16 | import { ConanProfileNodeProvider } from "./extension/ui/treeview/conanProfileProvider"; 17 | import { ConanRecipeNodeProvider } from "./extension/ui/treeview/conanRecipeProvider"; 18 | import { ConanRemoteNodeProvider } from "./extension/ui/treeview/conanRemoteProvider"; 19 | 20 | // This method is called when the extension is activated 21 | export function activate(context: vscode.ExtensionContext) { 22 | // Create VSConan extension channel 23 | // This channel is to show the command line outputs specifically for this extension 24 | var channelVSConan = vscode.window.createOutputChannel("VSConan"); 25 | 26 | // ========== Global Area Initialization 27 | // Global Area - The global area is stored under home folder ($HOME/.vsconan) 28 | // This area has lower priority then the workspace area 29 | // Global Area - To work for the API a extension folder will created in the home directory 30 | utils.vsconan.initializeGlobalArea(); 31 | 32 | // ========== Managing configuration 33 | initContextState(context); 34 | 35 | // Create Configuration Manager object to store and get some configuration 36 | let settingsPropertyManager = new SettingsPropertyManager(context, channelVSConan); 37 | 38 | let conanApiManager: ConanAPIManager = new ConanAPIManager(); 39 | 40 | // ========== Registering the treeview for the extension 41 | const conanRecipeNodeProvider = new ConanRecipeNodeProvider(conanApiManager, settingsPropertyManager); 42 | const conanProfileNodeProvider = new ConanProfileNodeProvider(conanApiManager); 43 | const conanPackageNodeProvider = new ConanPackageNodeProvider(conanApiManager, settingsPropertyManager); 44 | const conanPackageRevisionNodeProvider = new ConanPackageRevisionNodeProvider(conanApiManager, settingsPropertyManager); 45 | const conanRemoteNodeProvider = new ConanRemoteNodeProvider(conanApiManager); 46 | 47 | const conanCacheExplorerManager = new ConanCacheExplorerManager(context, channelVSConan, conanApiManager, settingsPropertyManager, conanRecipeNodeProvider, conanPackageNodeProvider, conanPackageRevisionNodeProvider); 48 | const conanProfileExplorerManager = new ConanProfileExplorerManager(context, channelVSConan, conanApiManager, conanProfileNodeProvider); 49 | const conanRemoteExplorerManager = new ConanRemoteExplorerManager(context, channelVSConan, conanApiManager, conanRemoteNodeProvider); 50 | const conanWorkspaceManager = new VSConanWorkspaceManager(context, channelVSConan, conanApiManager, settingsPropertyManager); 51 | 52 | const settingsManager = new SettingsManager(conanApiManager, 53 | conanCacheExplorerManager, 54 | conanProfileExplorerManager, 55 | conanRemoteExplorerManager, 56 | conanWorkspaceManager, 57 | settingsPropertyManager); 58 | 59 | settingsManager.init(); 60 | 61 | const configListener = vscode.workspace.onDidChangeConfiguration((event) => settingsManager.listen(event)); 62 | 63 | // Check if it starts with workspace 64 | // To check whether its workspace or not is to determine if the function "getWorkspaceFolder" returns undefined or a path 65 | // If user only open anyfile without a folder (as editor) this the workspace path will return "undefined" 66 | // This condition will be entered if vs code used as a workspace 67 | let wsList = vscode.workspace.workspaceFolders; 68 | 69 | // If it starts with workspace, there should be at least one element in the array of workspace folder 70 | if (wsList !== undefined) { 71 | 72 | for (let i = 0; i < wsList.length; i++) { 73 | 74 | let wsPath = wsList[i].uri.fsPath; 75 | let configPath = path.join(wsPath, constants.VSCONAN_FOLDER, constants.CONFIG_FILE); 76 | 77 | if (utils.conan.isFolderConanProject(wsPath) && !fs.existsSync(configPath!)) { 78 | 79 | vscode.window 80 | .showInformationMessage(`The workspace '${wsList[i].name}' is detected as a conan project. Do you want to configure this workspace?`, ...["Yes", "Not Now"]) 81 | .then((answer) => { 82 | if (answer === "Yes") { 83 | 84 | // .vsconan path in the workspace 85 | let vsconanPath = path.join(wsPath, constants.VSCONAN_FOLDER); 86 | if (!fs.existsSync(vsconanPath)) { 87 | fs.mkdirSync(vsconanPath!); 88 | } 89 | 90 | // Create a default config file 91 | utils.vsconan.config.createInitialWorkspaceConfig(vsconanPath); 92 | } 93 | }); 94 | } 95 | } 96 | } 97 | 98 | context.subscriptions.push( 99 | conanCacheExplorerManager, 100 | conanProfileExplorerManager, 101 | conanRemoteExplorerManager, 102 | conanWorkspaceManager, 103 | configListener 104 | ); 105 | } 106 | 107 | // this method is called when your extension is deactivated 108 | export function deactivate() { } 109 | 110 | /** 111 | * Function to initialize all the context state 112 | * @param context VS Code extension context 113 | */ 114 | function initContextState(context: vscode.ExtensionContext) { 115 | vscode.commands.executeCommand('setContext', 'show-dirty', vscode.workspace.getConfiguration("vsconan").get("explorer.treeview.package.showDirtyPackage")); 116 | context.workspaceState.update('show-dirty', vscode.workspace.getConfiguration("vsconan").get("explorer.treeview.package.showDirtyPackage")); 117 | 118 | vscode.commands.executeCommand('setContext', 'recipe-filtered', false); 119 | context.workspaceState.update('recipe-filtered', false); 120 | context.workspaceState.update('recipe-filter-key', ""); 121 | 122 | vscode.commands.executeCommand('setContext', 'package-filtered', false); 123 | context.workspaceState.update('package-filtered', false); 124 | context.workspaceState.update('package-filter-key', ""); 125 | } 126 | -------------------------------------------------------------------------------- /src/extension/disposable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This piece of code is a copy of solution from 'vscode-git-graph' project by Michael Hutchison 3 | * Link to repository https://github.com/mhutchie/vscode-git-graph 4 | * Link to the file https://github.com/mhutchie/vscode-git-graph/blob/develop/src/utils/disposable.ts 5 | */ 6 | 7 | import * as vscode from 'vscode'; 8 | 9 | export class Disposable implements vscode.Disposable { 10 | private disposables: vscode.Disposable[] = []; 11 | private disposed: boolean = false; 12 | 13 | /** 14 | * Disposes the resources used by the subclass. 15 | */ 16 | public dispose() { 17 | this.disposed = true; 18 | this.disposables.forEach((disposable) => { 19 | try { 20 | disposable.dispose(); 21 | } catch (_) { } 22 | }); 23 | this.disposables = []; 24 | } 25 | 26 | /** 27 | * Register a single disposable. 28 | */ 29 | protected registerDisposable(disposable: vscode.Disposable) { 30 | this.disposables.push(disposable); 31 | } 32 | 33 | /** 34 | * Register multiple disposables. 35 | */ 36 | protected registerDisposables(...disposables: vscode.Disposable[]) { 37 | this.disposables.push(...disposables); 38 | } 39 | 40 | /** 41 | * Is the Disposable disposed. 42 | * @returns `TRUE` => Disposable has been disposed, `FALSE` => Disposable hasn't been disposed. 43 | */ 44 | protected isDisposed() { 45 | return this.disposed; 46 | } 47 | } -------------------------------------------------------------------------------- /src/extension/manager/explorer/conanProfile.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { ConanAPIManager } from '../../../conans/api/conanAPIManager'; 3 | import * as utils from '../../../utils/utils'; 4 | import { ConanProfileItem, ConanProfileNodeProvider } from '../../ui/treeview/conanProfileProvider'; 5 | import { ExtensionManager } from "../extensionManager"; 6 | 7 | /** 8 | * Class to manage the treeview explorer of conan profiles 9 | */ 10 | export class ConanProfileExplorerManager extends ExtensionManager { 11 | 12 | private context: vscode.ExtensionContext; 13 | private outputChannel: vscode.OutputChannel; 14 | private conanApiManager: ConanAPIManager; 15 | private nodeProviderConanProfile: ConanProfileNodeProvider; 16 | private treeViewConanProfile: vscode.TreeView<any>; 17 | 18 | /** 19 | * Create conan profile explorer manager 20 | * @param context The context of the extension 21 | * @param outputChannel Output channel of the extension 22 | * @param conanApi Conan API 23 | * @param nodeProviderConanProfile Treedata provider for conan profile 24 | */ 25 | public constructor(context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel, conanApiManager: ConanAPIManager, nodeProviderConanProfile: ConanProfileNodeProvider) { 26 | super(); 27 | this.context = context; 28 | this.outputChannel = outputChannel; 29 | this.conanApiManager = conanApiManager; 30 | this.nodeProviderConanProfile = nodeProviderConanProfile; 31 | 32 | this.treeViewConanProfile = vscode.window.createTreeView("vsconan-explorer.treeview.profile", { 33 | treeDataProvider: this.nodeProviderConanProfile 34 | }); 35 | 36 | this.registerCommand("vsconan.explorer.treeview.profile.refresh", () => this.refreshProfileTreeview()); 37 | this.registerCommand("vsconan.explorer.treeview.profile.add", () => this.addProfile()); 38 | this.registerCommand("vsconan.explorer.treeview.profile.item.selected", () => this.showProfile()); 39 | this.registerCommand("vsconan.explorer.treeview.profile.item.remove", (node: ConanProfileItem) => this.removeProfile(node)); 40 | this.registerCommand("vsconan.explorer.treeview.profile.item.open-explorer", (node: ConanProfileItem) => this.openProfileInExplorer(node)); 41 | this.registerCommand("vsconan.explorer.treeview.profile.item.rename", (node: ConanProfileItem) => this.renameProfile(node)); 42 | this.registerCommand("vsconan.explorer.treeview.profile.item.duplicate", (node: ConanProfileItem) => this.duplicateProfile(node)); 43 | } 44 | 45 | public refresh() { 46 | this.refreshProfileTreeview(); 47 | } 48 | 49 | public clean () { 50 | this.nodeProviderConanProfile.refresh(); 51 | } 52 | 53 | /** 54 | * Refresh conan profile treeview 55 | */ 56 | private refreshProfileTreeview() { 57 | this.nodeProviderConanProfile.refresh(); 58 | } 59 | 60 | /** 61 | * Show the conan profile content in the editor 62 | */ 63 | private showProfile() { 64 | // Get the list of the profile from the treeview in string format 65 | let conanProfileList = this.nodeProviderConanProfile.getChildrenString(); 66 | 67 | // Get the selected profile name 68 | let profileName: string = this.treeViewConanProfile.selection[0].label; 69 | 70 | // This check is necessary since the refresh progress needs to be done manually. 71 | // TODO: This can be improved using watcher of the conan profile (considering the performance) 72 | if (conanProfileList.includes(profileName)) { 73 | utils.editor.openFileInEditor(this.conanApiManager.conanApi.getProfileFilePath(profileName)!); 74 | } 75 | else { 76 | vscode.window.showErrorMessage(`Unable to find the profile with name '${profileName}'.`); 77 | } 78 | } 79 | 80 | /** 81 | * Remove conan profile 82 | * @param node Selected conan profile node item 83 | */ 84 | private removeProfile(node: ConanProfileItem) { 85 | let conanProfileList = this.nodeProviderConanProfile.getChildrenString(); 86 | 87 | // Check if the profile still exists in the treeview, since the refresh progress needs to be done manually. 88 | if (conanProfileList.includes(node.label)) { 89 | vscode.window 90 | .showWarningMessage(`Are you sure you want to remove the profile '${node.label}'?`, ...["Yes", "No"]) 91 | .then((answer) => { 92 | if (answer === "Yes") { 93 | this.conanApiManager.conanApi.removeProfile(node.label); 94 | 95 | this.nodeProviderConanProfile.refresh(); 96 | } 97 | }); 98 | } 99 | else { 100 | vscode.window.showErrorMessage(`Unable to find the profile with name '${node.label}'.`); 101 | } 102 | } 103 | 104 | /** 105 | * Open profile in the file explorer 106 | * @param node Selected conan profile node item 107 | */ 108 | private openProfileInExplorer(node: ConanProfileItem) { 109 | let conanProfileList = this.nodeProviderConanProfile.getChildrenString(); 110 | 111 | if (conanProfileList.includes(node.label)) { 112 | vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(this.conanApiManager.conanApi.getProfileFilePath(node.label)!)); 113 | } 114 | else { 115 | vscode.window.showErrorMessage(`Unable to find the profile with name '${node.label}'.`); 116 | } 117 | } 118 | 119 | /** 120 | * Rename selected profile 121 | * @param node Selected conan profile node item 122 | */ 123 | private async renameProfile(node: ConanProfileItem) { 124 | let conanProfileList = this.nodeProviderConanProfile.getChildrenString(); 125 | 126 | if (conanProfileList.includes(node.label)) { 127 | 128 | const newProfileName = await vscode.window.showInputBox({ 129 | title: `Renaming profile ${node.label}. Enter a new name for the profile...`, 130 | placeHolder: node.label, 131 | validateInput: text => { 132 | if ((text === node.label || conanProfileList.includes(text)) && text !== "") { 133 | return 'Enter a different name'; 134 | } 135 | else if (text === "") { 136 | return "Enter a new name"; 137 | } 138 | else { 139 | return null; 140 | } 141 | } 142 | }); 143 | 144 | if (newProfileName) { 145 | try { 146 | this.conanApiManager.conanApi.renameProfile(node.label, newProfileName); 147 | this.nodeProviderConanProfile.refresh(); 148 | } 149 | catch (err) { 150 | vscode.window.showErrorMessage((err as Error).message); 151 | } 152 | } 153 | } 154 | else { 155 | vscode.window.showErrorMessage(`Unable to find the profile with name '${node.label}'.`); 156 | } 157 | } 158 | 159 | /** 160 | * Duplicate selected profile 161 | * @param node Selected conan profile node item 162 | */ 163 | private async duplicateProfile(node: ConanProfileItem) { 164 | let conanProfileList = this.nodeProviderConanProfile.getChildrenString(); 165 | 166 | if (conanProfileList.includes(node.label)) { 167 | 168 | const newProfileName = await vscode.window.showInputBox({ 169 | title: `Duplicating profile ${node.label}. Enter a new name for the profile...`, 170 | placeHolder: node.label, 171 | validateInput: text => { 172 | if ((text === node.label || conanProfileList.includes(text)) && text !== "") { 173 | return 'Enter a different name'; 174 | } 175 | else if (text === "") { 176 | return "Enter a new name"; 177 | } 178 | else { 179 | return null; 180 | } 181 | } 182 | }); 183 | 184 | if (newProfileName) { 185 | try { 186 | this.conanApiManager.conanApi.duplicateProfile(node.label, newProfileName); 187 | 188 | // Refresh the treeview once again 189 | this.nodeProviderConanProfile.refresh(); 190 | } 191 | catch (err) { 192 | vscode.window.showErrorMessage((err as Error).message); 193 | } 194 | } 195 | } 196 | else { 197 | vscode.window.showErrorMessage(`Unable to find the profile with name '${node.label}'.`); 198 | } 199 | } 200 | 201 | /** 202 | * Callback method to add a new profile 203 | */ 204 | private async addProfile() { 205 | this.refreshProfileTreeview(); 206 | let conanProfileList = this.nodeProviderConanProfile.getChildrenString(); 207 | 208 | const profileName = await vscode.window.showInputBox({ 209 | title: "Create a new Profile. Enter the name of the profile...", 210 | validateInput: text => { 211 | if (conanProfileList.includes(text) && text !== "") { 212 | return 'Profile with this name already exists.'; 213 | } 214 | else if (text === "") { 215 | return "Enter a name for the profile..."; 216 | } 217 | else { 218 | return null; 219 | } 220 | } 221 | }); 222 | 223 | if (profileName) { 224 | try { 225 | this.conanApiManager.conanApi.createNewProfile(profileName); 226 | 227 | // Refresh the treeview once again 228 | this.nodeProviderConanProfile.refresh(); 229 | } 230 | catch (err) { 231 | vscode.window.showErrorMessage((err as Error).message); 232 | } 233 | } 234 | } 235 | } -------------------------------------------------------------------------------- /src/extension/manager/explorer/conanRemote.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { ConanAPIManager } from '../../../conans/api/conanAPIManager'; 3 | import * as utils from '../../../utils/utils'; 4 | import { ConanRemoteItem, ConanRemoteNodeProvider } from '../../ui/treeview/conanRemoteProvider'; 5 | import { ExtensionManager } from "../extensionManager"; 6 | 7 | /** 8 | * Class to manage the treeview explorer of conan remotes 9 | */ 10 | export class ConanRemoteExplorerManager extends ExtensionManager { 11 | 12 | private context: vscode.ExtensionContext; 13 | private outputChannel: vscode.OutputChannel; 14 | private conanApiManager: ConanAPIManager; 15 | private nodeProviderConanRemote: ConanRemoteNodeProvider; 16 | private treeViewConanRemote: vscode.TreeView<any>; 17 | 18 | /** 19 | * Create conan remote explorer manager 20 | * @param context The context of the extension 21 | * @param outputChannel Output channel of the extension 22 | * @param conanApi Conan API 23 | * @param nodeProviderConanRemote Treedata provider for conan remote 24 | */ 25 | public constructor(context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel, conanApiManager: ConanAPIManager, nodeProviderConanRemote: ConanRemoteNodeProvider) { 26 | super(); 27 | 28 | this.context = context; 29 | this.outputChannel = outputChannel; 30 | this.conanApiManager = conanApiManager; 31 | this.nodeProviderConanRemote = nodeProviderConanRemote; 32 | 33 | this.treeViewConanRemote = vscode.window.createTreeView("vsconan-explorer.treeview.remote", { 34 | treeDataProvider: this.nodeProviderConanRemote 35 | }); 36 | 37 | this.registerCommand("vsconan.explorer.treeview.remote.refresh", () => this.refreshRemoteTreeview()); 38 | this.registerCommand("vsconan.explorer.treeview.remote.edit", () => this.editRemote()); 39 | this.registerCommand("vsconan.explorer.treeview.remote.add", () => this.addRemote()); 40 | this.registerCommand("vsconan.explorer.treeview.remote.item.remove", (node: ConanRemoteItem) => this.removeRemote(node)); 41 | this.registerCommand("vsconan.explorer.treeview.remote.item.enable", (node: ConanRemoteItem) => this.enableRemote(node)); 42 | this.registerCommand("vsconan.explorer.treeview.remote.item.disable", (node: ConanRemoteItem) => this.disableRemote(node)); 43 | this.registerCommand("vsconan.explorer.treeview.remote.item.rename", (node: ConanRemoteItem) => this.renameRemote(node)); 44 | this.registerCommand("vsconan.explorer.treeview.remote.item.update-url", (node: ConanRemoteItem) => this.updateRemoteURL(node)); 45 | } 46 | 47 | public refresh() { 48 | this.refreshRemoteTreeview(); 49 | } 50 | 51 | public clean() { 52 | this.nodeProviderConanRemote.refresh(); 53 | } 54 | 55 | /** 56 | * Refresh the treeview of conan remote 57 | */ 58 | private refreshRemoteTreeview() { 59 | this.nodeProviderConanRemote.refresh(); 60 | } 61 | 62 | /** 63 | * Edit the remotes.json file in the VS Code 64 | */ 65 | private editRemote() { 66 | let remoteFile = this.conanApiManager.conanApi.getRemoteFilePath(); 67 | 68 | if (remoteFile) { 69 | utils.editor.openFileInEditor(remoteFile); 70 | } 71 | else { 72 | vscode.window.showErrorMessage("Unable to find the file 'remotes.json'"); 73 | } 74 | } 75 | 76 | /** 77 | * Remove selected remote 78 | * @param node Selected conan remote node item 79 | */ 80 | private removeRemote(node: ConanRemoteItem) { 81 | let conanRemoteList = this.nodeProviderConanRemote.getChildrenString(); 82 | 83 | if (conanRemoteList.includes(node.label)) { 84 | vscode.window 85 | .showWarningMessage(`Are you sure you want to remove the remote '${node.label}'?`, ...["Yes", "No"]) 86 | .then((answer) => { 87 | if (answer === "Yes") { 88 | this.conanApiManager.conanApi.removeRemote(node.label); 89 | 90 | this.nodeProviderConanRemote.refresh(); 91 | } 92 | }); 93 | } 94 | else { 95 | vscode.window.showErrorMessage(`Unable to find the remote with name '${node.label}'.`); 96 | } 97 | } 98 | 99 | /** 100 | * Add a new remote 101 | */ 102 | private async addRemote() { 103 | this.refreshRemoteTreeview(); 104 | let conanRemoteList = this.nodeProviderConanRemote.getChildrenString(); 105 | 106 | const remoteName = await vscode.window.showInputBox({ 107 | title: "Add a new remote. Enter the name of the remote...", 108 | validateInput: text => { 109 | if (conanRemoteList.includes(text) && text !== "") { 110 | return 'Remote with this name already exists.'; 111 | } 112 | else if (text === "") { 113 | return "Enter a name for the remote..."; 114 | } 115 | else { 116 | return null; 117 | } 118 | } 119 | }); 120 | 121 | if (remoteName) { 122 | 123 | const remoteURL = await vscode.window.showInputBox({ 124 | title: `Add a new remote. Enter the URL for remote '${remoteName}'...`, 125 | validateInput: text => { 126 | if (text === "") { 127 | return "Enter a url for the remote..."; 128 | } 129 | else { 130 | return null; 131 | } 132 | } 133 | }); 134 | 135 | if (remoteURL) { 136 | try { 137 | this.conanApiManager.conanApi.addRemote(remoteName, remoteURL); 138 | 139 | // Refresh the treeview once again 140 | this.nodeProviderConanRemote.refresh(); 141 | } 142 | catch (err) { 143 | vscode.window.showErrorMessage((err as Error).message); 144 | } 145 | } 146 | } 147 | } 148 | 149 | /** 150 | * Enable selected remote 151 | * @param node Selected conan remote node item 152 | */ 153 | private enableRemote(node: ConanRemoteItem) { 154 | try { 155 | this.conanApiManager.conanApi.enableRemote(node.label, true); 156 | 157 | this.nodeProviderConanRemote.refresh(); 158 | } 159 | catch (err) { 160 | vscode.window.showErrorMessage((err as Error).message); 161 | } 162 | } 163 | 164 | /** 165 | * Disable selected remote 166 | * @param node Selected conan remote node item 167 | */ 168 | private disableRemote(node: ConanRemoteItem) { 169 | try { 170 | this.conanApiManager.conanApi.enableRemote(node.label, false); 171 | 172 | this.nodeProviderConanRemote.refresh(); 173 | } 174 | catch (err) { 175 | vscode.window.showErrorMessage((err as Error).message); 176 | } 177 | } 178 | 179 | /** 180 | * Rename selected remote 181 | * @param node Selected conan remote node item 182 | */ 183 | private async renameRemote(node: ConanRemoteItem) { 184 | let conanRemoteList = this.nodeProviderConanRemote.getChildrenString(); 185 | 186 | if (conanRemoteList.includes(node.label)) { 187 | 188 | const newRemoteName = await vscode.window.showInputBox({ 189 | title: `Renaming remote ${node.label}. Enter a new name for the remote...`, 190 | placeHolder: node.label, 191 | validateInput: text => { 192 | if ((text === node.label || conanRemoteList.includes(text)) && text !== "") { 193 | return 'Enter a different name'; 194 | } 195 | else if (text === "") { 196 | return "Enter a new name"; 197 | } 198 | else { 199 | return null; 200 | } 201 | } 202 | }); 203 | 204 | if (newRemoteName) { 205 | try { 206 | this.conanApiManager.conanApi.renameRemote(node.label, newRemoteName); 207 | this.nodeProviderConanRemote.refresh(); 208 | } 209 | catch (err) { 210 | vscode.window.showErrorMessage((err as Error).message); 211 | } 212 | } 213 | } 214 | else { 215 | vscode.window.showErrorMessage(`Unable to find the remote with name '${node.label}'.`); 216 | } 217 | } 218 | 219 | /** 220 | * Update URL of selected remote 221 | * @param node Selected conan remote node item 222 | */ 223 | private async updateRemoteURL(node: ConanRemoteItem) { 224 | let conanRemoteList = this.nodeProviderConanRemote.getChildrenString(); 225 | 226 | if (conanRemoteList.includes(node.label)) { 227 | 228 | // let remoteDetailInfo = JSON.parse(node.detailInfo); 229 | 230 | const newURL = await vscode.window.showInputBox({ 231 | title: `Update URL for remote ${node.label}. Enter a new URL for the remote...`, 232 | placeHolder: node.model.url, 233 | validateInput: text => { 234 | if (text === node.model.url && text !== "") { 235 | return 'Enter a differet URL'; 236 | } 237 | else if (text === "") { 238 | return "Enter a URL"; 239 | } 240 | else { 241 | return null; 242 | } 243 | } 244 | }); 245 | 246 | if (newURL) { 247 | try { 248 | this.conanApiManager.conanApi.updateRemoteURL(node.label, newURL); 249 | this.nodeProviderConanRemote.refresh(); 250 | } 251 | catch (err) { 252 | vscode.window.showErrorMessage((err as Error).message); 253 | } 254 | } 255 | } 256 | else { 257 | vscode.window.showErrorMessage(`Unable to find the remote with name '${node.label}'.`); 258 | } 259 | } 260 | } -------------------------------------------------------------------------------- /src/extension/manager/extensionManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { Disposable } from "../disposable"; 3 | 4 | export abstract class ExtensionManager extends Disposable { 5 | /** 6 | * Register VS Code extension commands that are defined in the package.json 7 | * @param command A unique identifier for the command. 8 | * @param callback A command handler function. 9 | */ 10 | protected registerCommand(command: string, callback: (...args: any[]) => any) { 11 | this.registerDisposable( 12 | vscode.commands.registerCommand(command, (...args: any[]) => { 13 | callback(...args); 14 | }) 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/extension/manager/workspaceEnvironment.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as vscode from 'vscode'; 3 | import * as utils from '../../utils/utils'; 4 | import { SettingsPropertyManager } from '../settings/settingsPropertyManager'; 5 | import path = require('path'); 6 | 7 | /** 8 | * Shorthand type for Array of "key=value" pairs of environment variables. 9 | */ 10 | type EnvVars = Array<[string, string | undefined]>; 11 | 12 | /** 13 | * Tag workspace Metadata with version for easy upgrades. 14 | */ 15 | const activeEnvVersion: number = 1; 16 | type ActiveEnv = [version: number, configName: string, conanEnv: utils.conan.ConanEnv, envValues: [string, string][]]; 17 | 18 | 19 | /** 20 | * Manage VSCode's process and terminal environment. 21 | */ 22 | export class VSConanWorkspaceEnvironment { 23 | private context: vscode.ExtensionContext; 24 | private settingsPropertyManager: SettingsPropertyManager; 25 | private outputChannel: vscode.OutputChannel; 26 | 27 | public constructor(context: vscode.ExtensionContext, settingsPropertyManager: SettingsPropertyManager, outputChannel: vscode.OutputChannel) { 28 | this.context = context; 29 | this.settingsPropertyManager = settingsPropertyManager; 30 | this.outputChannel = outputChannel; 31 | 32 | const activeEnv = this.activeEnv(); 33 | if (activeEnv) { 34 | this.updateVSCodeEnvironment(activeEnv[3]); 35 | } 36 | } 37 | 38 | /** 39 | * Extend VSCode's environment by environment variables from Conan. 40 | * 41 | * @param conanEnv Which Conan environment to activate 42 | * @param configName Config name 43 | * @param pythonInterpreter Path to python interpreter 44 | * @param args Additional Conan arguments as given to `conan install` 45 | */ 46 | public async activateEnvironment(conanEnv: utils.conan.ConanEnv, configName: string, pythonInterpreter: string, args: string[]) { 47 | this.restoreEnvironment(); 48 | var newenv = await utils.conan.readEnvFromConan(conanEnv, pythonInterpreter, args); 49 | this.updateBackupEnvironment(newenv); 50 | 51 | this.updateVSCodeEnvironment(newenv); 52 | const activeEnv: ActiveEnv = [activeEnvVersion, configName, conanEnv, newenv]; 53 | await this.context.workspaceState.update("vsconan.activeEnv", activeEnv); 54 | if (vscode.env.remoteName === undefined) { 55 | await vscode.commands.executeCommand('workbench.action.restartExtensionHost'); 56 | } else { 57 | await vscode.commands.executeCommand('workbench.action.reloadWindow'); 58 | } 59 | await this.outputChannel.appendLine(`Activate ${conanEnv}: ${JSON.stringify(newenv, null, 2)}`); 60 | await vscode.window.showInformationMessage(`Activated Environment ${configName}[${conanEnv}]`); 61 | } 62 | 63 | /** 64 | * Restore VSCode environment using backup. 65 | */ 66 | public restoreEnvironment() { 67 | const backupEnv = this.context.workspaceState.get<EnvVars>("vsconan.backupEnv"); 68 | console.log(`[vsconan] restoreEnvironment: ${backupEnv}`); 69 | if (backupEnv) { 70 | this.updateVSCodeEnvironment(backupEnv); 71 | } 72 | this.updateDotEnvFile([]); 73 | this.context.workspaceState.update("vsconan.activeEnv", undefined); 74 | } 75 | 76 | /** 77 | * Update backup environment by saving all _current_ environment variables 78 | * which would be modified by `newenv`. 79 | * 80 | * @param newenv New environment. Not the backup! 81 | */ 82 | private updateBackupEnvironment(newenv: EnvVars) { 83 | let backupEnv = new Map(this.context.workspaceState.get<EnvVars>("vsconan.backupEnv")); 84 | let newBackupEnv: EnvVars = []; 85 | newenv.forEach(([key, _]) => { 86 | if (backupEnv.has(key)) { 87 | newBackupEnv.push([key, backupEnv.get(key)]); 88 | } else { 89 | // TODO: Take really from process env?? 90 | newBackupEnv.push([key, process.env[key]]); 91 | } 92 | }); 93 | this.context.workspaceState.update("vsconan.backupEnv", newBackupEnv); 94 | console.log(`[vsconan] updateBackupEnvironment: ${newBackupEnv}`); 95 | } 96 | 97 | /** 98 | * Update VSCode's process and terminal environment. 99 | * 100 | * @param data Environment variables to apply 101 | */ 102 | private updateVSCodeEnvironment(data: EnvVars) { 103 | console.log(`[vsconan] updateVSCodeEnvironment: ${data}`); 104 | data.forEach(([key, value]) => { 105 | if (!value) { 106 | delete process.env[key]; 107 | this.context.environmentVariableCollection.delete(key); 108 | } else { 109 | process.env[key] = value; 110 | this.context.environmentVariableCollection.replace(key, value); 111 | } 112 | }); 113 | 114 | this.updateDotEnvFile(data); 115 | } 116 | 117 | /** 118 | * Update `.env`-File in current Workspace if option is selected. 119 | * 120 | * @param data New content 121 | */ 122 | private updateDotEnvFile(data: EnvVars) { 123 | if (this.settingsPropertyManager.isUpdateDotEnv() !== true) { 124 | return; 125 | } 126 | let ws = utils.workspace.selectWorkspace(); 127 | ws.then(result => { 128 | const dotenv = path.join(String(result), ".env"); 129 | const content = data.map(([key, value]) => { 130 | return `${key}=${value}`; 131 | }).join('\n'); 132 | fs.writeFileSync(dotenv, content); 133 | }).catch(reject => { 134 | vscode.window.showInformationMessage("No workspace detected."); 135 | }); 136 | } 137 | 138 | public activeEnv(): ActiveEnv | undefined { 139 | const activeEnv = this.context.workspaceState.get<ActiveEnv>("vsconan.activeEnv"); 140 | if (activeEnv?.[0] === activeEnvVersion) { 141 | return activeEnv; 142 | } 143 | return undefined; 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/extension/settings/model.ts: -------------------------------------------------------------------------------- 1 | 2 | export class ConanProfileConfiguration { 3 | conanVersion: string = ""; 4 | conanPythonInterpreter: string = ""; 5 | conanExecutable: string = ""; 6 | conanExecutionMode: string = ""; 7 | conanUserHome: string | null | undefined = undefined; 8 | 9 | public isValid(): boolean { 10 | let valid: boolean = false; 11 | 12 | if ((this.conanVersion === "1" || this.conanVersion === "2") && 13 | (this.conanExecutionMode === "conanExecutable" || this.conanExecutionMode === "pythonInterpreter")) { 14 | valid = true; 15 | } 16 | 17 | return valid; 18 | } 19 | 20 | /** 21 | * Public function of the model to escaping the whitespace inside that path string. 22 | */ 23 | public escapeWhitespace(): void { 24 | if (this.conanPythonInterpreter) { 25 | this.conanPythonInterpreter = JSON.stringify(this.conanPythonInterpreter); 26 | } 27 | 28 | if (this.conanExecutable) { 29 | this.conanExecutable = JSON.stringify(this.conanExecutable); 30 | } 31 | 32 | if (this.conanUserHome) { 33 | this.conanUserHome = JSON.stringify(this.conanUserHome); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/extension/settings/settingsManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { ConanExecutionMode } from "../../conans/api/base/conanAPI"; 3 | import { ConanAPIManager } from "../../conans/api/conanAPIManager"; 4 | import { ConanCacheExplorerManager } from "../manager/explorer/conanCache"; 5 | import { ConanProfileExplorerManager } from "../manager/explorer/conanProfile"; 6 | import { ConanRemoteExplorerManager } from "../manager/explorer/conanRemote"; 7 | import { VSConanWorkspaceManager } from "../manager/vsconanWorkspace"; 8 | import { SettingsPropertyManager } from "./settingsPropertyManager"; 9 | 10 | export class SettingsManager { 11 | private conanApiManager: ConanAPIManager; 12 | private conanCacheExplorerManager: ConanCacheExplorerManager; 13 | private conanProfileExplorerManager: ConanProfileExplorerManager; 14 | private conanRemoteExplorerManager: ConanRemoteExplorerManager; 15 | private conanWorkspaceManager: VSConanWorkspaceManager; 16 | private settingsPropertyManager: SettingsPropertyManager; 17 | 18 | public constructor( 19 | conanApiManager: ConanAPIManager, 20 | conanCacheExplorerManager: ConanCacheExplorerManager, 21 | conanProfileExplorerManager: ConanProfileExplorerManager, 22 | conanRemoteExplorerManager: ConanRemoteExplorerManager, 23 | conanWorkspaceManager: VSConanWorkspaceManager, 24 | settingsPropertyManager: SettingsPropertyManager 25 | ) { 26 | this.conanApiManager = conanApiManager; 27 | this.conanCacheExplorerManager = conanCacheExplorerManager; 28 | this.conanProfileExplorerManager = conanProfileExplorerManager; 29 | this.conanRemoteExplorerManager = conanRemoteExplorerManager; 30 | this.conanWorkspaceManager = conanWorkspaceManager; 31 | this.settingsPropertyManager = settingsPropertyManager; 32 | } 33 | 34 | public init() { 35 | // Set the environment conan user home path in the config manager 36 | // With this approach, we can get back to the environment variable that is set when the VS Code is started 37 | // Undefined means no specific path is set, so conan default home folder will be used. 38 | this.settingsPropertyManager.setEnvConanUserHome(process.env.CONAN_USER_HOME); 39 | this.settingsPropertyManager.setEnvConanHome(process.env.CONAN_HOME); 40 | 41 | 42 | // // Get the configuration from 'settings.json' for this matter 43 | // let conanUserHome: string | null | undefined = vscode.workspace.getConfiguration("vsconan").get("general.conanUserHome"); 44 | // // If this is defined, the the environment variable will be overwritten, using the configuration in settings.json 45 | // if (conanUserHome !== null) { 46 | // process.env.CONAN_USER_HOME = conanUserHome; 47 | // } 48 | 49 | // TODO: Check if vsconan.conan.profile.configurations is defined at all, if not define default value 50 | 51 | 52 | // TODO: vsconan.conan.profile.default is defined? if not set to default 53 | 54 | this.changeConanProfile(); 55 | } 56 | 57 | public listen(event: vscode.ConfigurationChangeEvent) { 58 | // Check if only vsconan configuration is changed, otherwise do nothing. Removing overhead. 59 | if (event.affectsConfiguration("vsconan")) { 60 | if (event.affectsConfiguration("vsconan.conan.profile")) { 61 | this.changeConanProfile(); 62 | } 63 | } 64 | } 65 | 66 | private async changeConanProfile() { 67 | let selectedProfile: string | undefined = vscode.workspace.getConfiguration("vsconan.conan.profile").get("default"); 68 | 69 | if (this.settingsPropertyManager.isProfileAvailable(selectedProfile!) && 70 | await this.settingsPropertyManager.isProfileValid(selectedProfile!)) { 71 | let profileObject = await this.settingsPropertyManager.getConanProfileObject(selectedProfile!); 72 | 73 | let conanExecutionMode: ConanExecutionMode = ConanExecutionMode.conan; 74 | 75 | if (profileObject!.conanExecutionMode === "pythonInterpreter") { 76 | conanExecutionMode = ConanExecutionMode.python; 77 | } 78 | else if (profileObject!.conanExecutionMode === "conanExecutable") { 79 | conanExecutionMode = ConanExecutionMode.conan; 80 | } 81 | 82 | this.conanApiManager.setApiInstance( 83 | profileObject!.conanVersion, 84 | profileObject!.conanPythonInterpreter, 85 | profileObject!.conanExecutable, 86 | conanExecutionMode); 87 | 88 | this.conanCacheExplorerManager.refresh(); 89 | this.conanProfileExplorerManager.refresh(); 90 | this.conanRemoteExplorerManager.refresh(); 91 | this.conanWorkspaceManager.refresh(); 92 | } 93 | else { 94 | this.conanApiManager.setApiInstance( 95 | "", 96 | "", 97 | "", 98 | ConanExecutionMode.conan, 99 | ); 100 | 101 | vscode.window.showErrorMessage("VSConan - Invalid configuration for conan. Check the configuration once again."); 102 | 103 | this.conanCacheExplorerManager.clean(); 104 | this.conanProfileExplorerManager.clean(); 105 | this.conanRemoteExplorerManager.clean(); 106 | this.conanWorkspaceManager.refresh(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/extension/settings/settingsPropertyManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | 3 | import { existsSync } from "fs"; 4 | import { general, python } from "../../utils/utils"; 5 | import { ConanProfileConfiguration } from "./model"; 6 | import path = require("path"); 7 | 8 | /** 9 | * Class to manage the extension configuration 10 | * This class actually only abstracts the usage of VS Code configuration API 11 | */ 12 | export class SettingsPropertyManager { 13 | private context: vscode.ExtensionContext; 14 | 15 | // This variable is used to store the original path from the environment 16 | // If there is no environment variable defined the path will be an empty string. 17 | private envConanUserHome: string | undefined = undefined; // CONAN 1 18 | private envConanHome: string | undefined = undefined; // CONAN 2 19 | private outputChannel: vscode.OutputChannel | undefined = undefined; 20 | 21 | public constructor(context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel) { 22 | this.context = context; 23 | this.outputChannel = outputChannel; 24 | } 25 | 26 | public setEnvConanUserHome(envConanUserHome: string | undefined) { 27 | this.envConanUserHome = envConanUserHome; 28 | } 29 | 30 | public getEnvConanUserHome(): string | undefined { 31 | return this.envConanUserHome; 32 | } 33 | 34 | public setEnvConanHome(envConanHome: string | undefined) { 35 | this.envConanHome = envConanHome; 36 | } 37 | 38 | public getEnvConanHome(): string | undefined { 39 | return this.envConanHome; 40 | } 41 | 42 | public showDirtyPackage(): boolean | undefined { 43 | return vscode.workspace.getConfiguration("vsconan").get("explorer.treeview.package.showDirtyPackage"); 44 | } 45 | 46 | public isRecipeFiltered(): boolean { 47 | return this.context.workspaceState.get('recipe-filtered')!; 48 | } 49 | 50 | public setRecipeFilter(filterKey: string) { 51 | vscode.commands.executeCommand('setContext', 'recipe-filtered', true); 52 | this.context.workspaceState.update('recipe-filtered', true); 53 | this.context.workspaceState.update("recipe-filter-key", filterKey); 54 | } 55 | 56 | public clearRecipeFilter() { 57 | vscode.commands.executeCommand('setContext', 'recipe-filtered', false); 58 | this.context.workspaceState.update('recipe-filtered', false); 59 | this.context.workspaceState.update('recipe-filter-key', ""); 60 | } 61 | 62 | public getRecipeFilterKey(): string | undefined { 63 | return this.context.workspaceState.get('recipe-filter-key'); 64 | } 65 | 66 | public isPackageFiltered(): boolean { 67 | return this.context.workspaceState.get('package-filtered')!; 68 | } 69 | 70 | public setPackageFilter(filterKey: string) { 71 | vscode.commands.executeCommand('setContext', 'package-filtered', true); 72 | this.context.workspaceState.update('package-filtered', true); 73 | this.context.workspaceState.update("package-filter-key", filterKey); 74 | } 75 | 76 | public clearPackageFilter() { 77 | vscode.commands.executeCommand('setContext', 'package-filtered', false); 78 | this.context.workspaceState.update('package-filtered', false); 79 | this.context.workspaceState.update('package-filter-key', ""); 80 | } 81 | 82 | public getPackageFilterKey(): string | undefined { 83 | return this.context.workspaceState.get('package-filter-key'); 84 | } 85 | 86 | public getListOfConanProfiles(): Array<string> { 87 | let listOfConanProfile: Array<string> = []; 88 | 89 | // Get the object list with key value maps of the proxy objects 90 | let profileConfigurations = vscode.workspace.getConfiguration("vsconan.conan.profile").get("configurations"); 91 | 92 | // Convert to generic objects 93 | let profileConfigurationsObjectList: Object = Object.assign({}, profileConfigurations); 94 | 95 | listOfConanProfile = Object.keys(profileConfigurationsObjectList); 96 | 97 | return listOfConanProfile; 98 | } 99 | 100 | public async getConanProfileObject(profileName: string): Promise<ConanProfileConfiguration | undefined> { 101 | 102 | let profileObject: ConanProfileConfiguration | undefined = undefined; 103 | 104 | let profileConfigurations = vscode.workspace.getConfiguration("vsconan.conan.profile").get("configurations"); 105 | 106 | let profileConfigurationsObject: Object = Object.assign({}, profileConfigurations); 107 | 108 | if (profileConfigurationsObject.hasOwnProperty(profileName!)) { 109 | let selectedProfileObject: Object = Object.assign({}, profileConfigurationsObject[profileName as keyof typeof profileConfigurationsObject]); 110 | 111 | profileObject = general.plainObjectToClass(ConanProfileConfiguration, selectedProfileObject); 112 | profileObject.escapeWhitespace(); 113 | } 114 | 115 | if (profileObject) { 116 | let pythonInterpreter = await python.getCurrentPythonInterpreter(); 117 | if (pythonInterpreter !== undefined) { 118 | if (!profileObject.conanPythonInterpreter) { 119 | profileObject.conanPythonInterpreter = pythonInterpreter; 120 | } 121 | if (!profileObject.conanExecutable) { 122 | const conanExecutable = path.join(path.dirname(pythonInterpreter), "conan" + (process.platform === "win32" ? ".exe" : "")); 123 | if (existsSync(conanExecutable)) { 124 | profileObject.conanExecutable = conanExecutable; 125 | } 126 | } 127 | this.outputChannel?.append(`Using Python interpreter: ${profileObject.conanPythonInterpreter}\n`); 128 | this.outputChannel?.append(`Using Conan executable: ${profileObject.conanExecutable}\n`); 129 | } 130 | } 131 | 132 | let pythonInterpreter = await python.getCurrentPythonInterpreter(); 133 | if (pythonInterpreter !== undefined) { 134 | if (!profileObject?.conanPythonInterpreter) { 135 | profileObject!.conanPythonInterpreter = pythonInterpreter; 136 | } 137 | if (!profileObject?.conanExecutable) { 138 | const exePostfix = process.platform === 'win32' ? '.exe' : ''; 139 | profileObject!.conanExecutable = path.join(path.dirname(pythonInterpreter), `conan${exePostfix}`); 140 | } 141 | } 142 | 143 | return profileObject; 144 | } 145 | 146 | public getSelectedConanProfile(): string | undefined { 147 | return vscode.workspace.getConfiguration("vsconan.conan.profile").get("default"); 148 | } 149 | 150 | public async isProfileValid(profileName: string): Promise<boolean> { 151 | let valid: boolean = false; 152 | 153 | let profileObject: ConanProfileConfiguration | undefined = await this.getConanProfileObject(profileName); 154 | 155 | if ((profileObject) && (profileObject.isValid())) { 156 | valid = true; 157 | } 158 | 159 | return valid; 160 | } 161 | 162 | public async getConanVersionOfProfile(profileName: string): Promise<string | null> { 163 | 164 | let conanVersion: string | null = null; 165 | 166 | let profileObject: ConanProfileConfiguration | undefined = await this.getConanProfileObject(profileName); 167 | 168 | if ((profileObject) && (profileObject.isValid())) { 169 | conanVersion = profileObject.conanVersion; 170 | } 171 | 172 | return conanVersion; 173 | } 174 | 175 | public updateConanProfile(profileName: string | undefined) { 176 | vscode.workspace.getConfiguration("vsconan.conan.profile").update("default", profileName); 177 | } 178 | 179 | public isProfileAvailable(profileName: string): boolean { 180 | let isAvailable: boolean = false; 181 | 182 | let conanProfileList: Array<string> = this.getListOfConanProfiles(); 183 | 184 | if (conanProfileList.includes(profileName)) { 185 | isAvailable = true; 186 | } 187 | 188 | return isAvailable; 189 | } 190 | 191 | public isUpdateDotEnv(): boolean | undefined { 192 | return vscode.workspace.getConfiguration("vsconan.conan").get("env.dotenv"); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/extension/ui/treeview/conanPackageProvider.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as vscode from 'vscode'; 3 | import { ConanAPIManager } from '../../../conans/api/conanAPIManager'; 4 | import { ConanPackage } from '../../../conans/model/conanPackage'; 5 | import { SettingsPropertyManager } from '../../settings/settingsPropertyManager'; 6 | import { ConanAPI } from '../../../conans/api/base/conanAPI'; 7 | 8 | export class ConanPackageNodeProvider implements vscode.TreeDataProvider<ConanPackageItem> { 9 | 10 | private _onDidChangeTreeData: vscode.EventEmitter<ConanPackageItem | undefined | void> = new vscode.EventEmitter<ConanPackageItem | undefined | void>(); 11 | readonly onDidChangeTreeData: vscode.Event<ConanPackageItem | undefined | void> = this._onDidChangeTreeData.event; 12 | 13 | private recipeName: string = ""; 14 | private showDirtyPackage: boolean = false; 15 | private conanApiManager: ConanAPIManager; 16 | private settingsPropertyManager: SettingsPropertyManager; 17 | 18 | private selectedPackage: string | undefined = undefined; 19 | 20 | public constructor(conanApiManager: ConanAPIManager, settingsPropertyManager: SettingsPropertyManager) { 21 | this.conanApiManager = conanApiManager; 22 | this.settingsPropertyManager = settingsPropertyManager; 23 | } 24 | 25 | public refresh(recipeName: string, showDirtyPackage: boolean): void { 26 | this.recipeName = recipeName; 27 | this.showDirtyPackage = showDirtyPackage; 28 | this._onDidChangeTreeData.fire(); 29 | } 30 | 31 | public getTreeItem(element: ConanPackageItem): vscode.TreeItem { 32 | return element; 33 | } 34 | 35 | public getChildren(element?: ConanPackageItem): ConanPackageItem[] { 36 | let packageList: Array<ConanPackage> = []; 37 | let dirtyPackageList: Array<ConanPackage> = []; 38 | let packageItemList: Array<ConanPackageItem> = []; 39 | 40 | if (this.conanApiManager.conanApi) { 41 | if (this.settingsPropertyManager.isPackageFiltered()) { 42 | packageList = this.conanApiManager.conanApi.getPackagesByRemote(this.recipeName, this.settingsPropertyManager.getPackageFilterKey()!); 43 | } 44 | else { 45 | packageList = this.conanApiManager.conanApi.getPackages(this.recipeName); 46 | } 47 | 48 | if (this.showDirtyPackage) { 49 | dirtyPackageList = this.conanApiManager.conanApi.getDirtyPackage(this.recipeName); 50 | } 51 | 52 | for (let pkg of packageList) { 53 | packageItemList.push(new ConanPackageItem(pkg.id, vscode.TreeItemCollapsibleState.None, pkg)); 54 | } 55 | 56 | for (let pkg of dirtyPackageList) { 57 | packageItemList.push(new ConanPackageItem(pkg.id, vscode.TreeItemCollapsibleState.None, pkg)); 58 | } 59 | } 60 | 61 | return packageItemList; 62 | } 63 | 64 | public getChildrenString(): string[] { 65 | let childStringList = []; 66 | 67 | for (let child of this.getChildren()) { 68 | childStringList.push(child.label); 69 | } 70 | 71 | return childStringList; 72 | } 73 | 74 | public setSelectedPackage(packageId: string | undefined) { 75 | this.selectedPackage = packageId; 76 | } 77 | 78 | public getSelectedPackage(): string { 79 | return this.selectedPackage!; 80 | } 81 | } 82 | 83 | export class ConanPackageItem extends vscode.TreeItem { 84 | public model: ConanPackage; 85 | 86 | constructor( 87 | public readonly label: string, 88 | public readonly collapsibleState: vscode.TreeItemCollapsibleState, 89 | model: ConanPackage) { 90 | 91 | super(label, collapsibleState); 92 | 93 | this.model = model; 94 | 95 | this.command = { 96 | "title": "Conan Package Selected", 97 | "command": "vsconan.explorer.treeview.package.item.selected", 98 | }; 99 | 100 | if (this.model.dirty) { 101 | this.iconPath = { 102 | light: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'package_dirty.png')), 103 | dark: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'package_dirty.png')) 104 | }; 105 | 106 | this.contextValue = 'packageDirty'; 107 | } 108 | else { 109 | this.iconPath = { 110 | light: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'package.png')), 111 | dark: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'package.png')) 112 | }; 113 | 114 | this.contextValue = 'package'; 115 | } 116 | } 117 | 118 | public isDirty(): boolean { 119 | return this.model.dirty; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/extension/ui/treeview/conanPackageRevisionProvider.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as vscode from 'vscode'; 3 | import * as fs from 'fs'; 4 | import { ConanAPIManager } from '../../../conans/api/conanAPIManager'; 5 | import { ConanPackageRevision } from '../../../conans/model/conanPackageRevision'; 6 | import { SettingsPropertyManager } from '../../settings/settingsPropertyManager'; 7 | 8 | export class ConanPackageRevisionNodeProvider implements vscode.TreeDataProvider<ConanPackageRevisionItem> { 9 | 10 | private _onDidChangeTreeData: vscode.EventEmitter<ConanPackageRevisionItem | undefined | void> = new vscode.EventEmitter<ConanPackageRevisionItem | undefined | void>(); 11 | readonly onDidChangeTreeData: vscode.Event<ConanPackageRevisionItem | undefined | void> = this._onDidChangeTreeData.event; 12 | 13 | private recipeName: string = ""; 14 | private packageId: string = ""; 15 | private showDirtyPackage: boolean = false; 16 | private conanApiManager: ConanAPIManager; 17 | private settingsPropertyManager: SettingsPropertyManager; 18 | 19 | private expandedNodes = new Set<string>(); 20 | 21 | public constructor(conanApiManager: ConanAPIManager, settingsPropertyManager: SettingsPropertyManager) { 22 | this.conanApiManager = conanApiManager; 23 | this.settingsPropertyManager = settingsPropertyManager; 24 | } 25 | 26 | /** 27 | * Refreshes the tree view with the provided recipe name, package ID, and dirty package flag. 28 | * 29 | * @param recipeName - The name of the recipe to refresh. 30 | * @param packageId - The ID of the package to refresh. 31 | * @param showDirtyPackage - Flag to indicate whether to show dirty packages. 32 | */ 33 | public refresh(recipeName: string, packageId: string, showDirtyPackage: boolean): void { 34 | this.recipeName = recipeName; 35 | this.packageId = packageId; 36 | this.showDirtyPackage = showDirtyPackage; 37 | this._onDidChangeTreeData.fire(); 38 | } 39 | 40 | public getTreeItem(element: ConanPackageRevisionItem): vscode.TreeItem { 41 | return element; 42 | } 43 | 44 | public getChildren(element?: ConanPackageRevisionItem): ConanPackageRevisionItem[] { 45 | 46 | if (!element) { 47 | 48 | let packageRevisionList: Array<ConanPackageRevision> = []; 49 | // TODO: Implement dirty package list 50 | let dirtyPackageList: Array<ConanPackageRevision> = []; 51 | let packageRevisionItemList: Array<ConanPackageRevisionItem> = []; 52 | 53 | if (this.conanApiManager.conanApi) { 54 | packageRevisionList = this.conanApiManager.conanApi.getPackageRevisions(this.recipeName, this.packageId); 55 | 56 | for (let pkgRevision of packageRevisionList) { 57 | let packageRevisionPath = this.conanApiManager.conanApi.getPackageRevisionPath(this.recipeName, this.packageId, pkgRevision.id); 58 | 59 | 60 | packageRevisionItemList.push(new ConanPackageRevisionItem(pkgRevision.id, 61 | vscode.Uri.file(packageRevisionPath!), 62 | vscode.TreeItemCollapsibleState.Collapsed, 63 | pkgRevision, true, true)); 64 | } 65 | } 66 | 67 | return packageRevisionItemList; 68 | } 69 | 70 | // Load subfolders/files only if the folder is expanded 71 | if (!this.expandedNodes.has(element.resourceUri.fsPath)) { 72 | return []; 73 | } 74 | 75 | return this.getFilesAndFolders(element.resourceUri.fsPath); 76 | 77 | } 78 | 79 | /** 80 | * Method to get files and folders from a directory of the package revision path. 81 | * It reads the directory contents, creates `ConanPackageRevisionItem` for each file/folder, 82 | * and sorts them with folders first. 83 | * 84 | * @param directoryPath - The path of the directory to read. 85 | * @returns An array of `ConanPackageRevisionItem` representing the files and folders. 86 | */ 87 | private getFilesAndFolders(directoryPath: string): ConanPackageRevisionItem[] { 88 | 89 | // Check if the directory exists 90 | if (!fs.existsSync(directoryPath)) { 91 | return []; 92 | } 93 | 94 | // Read the directory contents and return the tree items 95 | const files = fs.readdirSync(directoryPath).map(file => { 96 | const filePath = path.join(directoryPath, file); 97 | const stat = fs.statSync(filePath); 98 | 99 | return new ConanPackageRevisionItem( 100 | file, 101 | vscode.Uri.file(filePath), 102 | stat.isDirectory() ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None, 103 | new ConanPackageRevision("", 123), 104 | false, 105 | stat.isDirectory() 106 | ); 107 | }); 108 | 109 | // Sort: Folders first, then files 110 | files.sort((a, b) => Number(b.isDirectory) - Number(a.isDirectory)); 111 | 112 | return files; 113 | } 114 | 115 | public getChildrenString(): string[] { 116 | let childStringList = []; 117 | 118 | for (let child of this.getChildren()) { 119 | childStringList.push(child.label); 120 | } 121 | 122 | return childStringList; 123 | } 124 | 125 | expandFolder(uri: vscode.Uri): void { 126 | this.expandedNodes.add(uri.fsPath); 127 | this.refresh(this.recipeName, this.packageId, this.showDirtyPackage); 128 | } 129 | } 130 | 131 | export class ConanPackageRevisionItem extends vscode.TreeItem { 132 | public model!: ConanPackageRevision; 133 | isDirectory: boolean; // To check if it's a folder 134 | 135 | constructor(public readonly label: string, public readonly resourceUri: vscode.Uri, public readonly collapsibleState: vscode.TreeItemCollapsibleState, model: ConanPackageRevision, isRoot: boolean, isDirectory: boolean = false) { 136 | 137 | super(resourceUri = resourceUri, collapsibleState = collapsibleState); 138 | 139 | this.label = label; 140 | 141 | // Internal state to determine if this item is a folder 142 | this.isDirectory = isDirectory; 143 | 144 | this.command = { 145 | "title": "Conan Package Revision Selected", 146 | "command": "vsconan.explorer.treeview.package.revision.item.selected", 147 | }; 148 | 149 | if (isRoot) { 150 | // Conan package revision is now expandable to be able to see the content of the package revision 151 | // This check is necessary to check if the item is the root of the package revision treeview item 152 | 153 | 154 | // In case fo the root item, the icon is set to the package icon 155 | this.iconPath = { 156 | light: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'package_revision.png')), 157 | dark: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'package_revision.png')) 158 | }; 159 | 160 | // Set the context to show all the buttons that are pre-configured by the `package.json` file. 161 | this.contextValue = 'packageRevision'; 162 | 163 | 164 | // Feed model with package revision data 165 | this.model = model; 166 | 167 | // Set the tooltip to show the package revision data in JSON format 168 | this.tooltip = JSON.stringify(this.model, null, 4); 169 | } 170 | 171 | 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/extension/ui/treeview/conanProfileProvider.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as vscode from 'vscode'; 3 | import { ConanAPIManager } from '../../../conans/api/conanAPIManager'; 4 | 5 | export class ConanProfileNodeProvider implements vscode.TreeDataProvider<ConanProfileItem> { 6 | 7 | private _onDidChangeTreeData: vscode.EventEmitter<ConanProfileItem | undefined | void> = new vscode.EventEmitter<ConanProfileItem | undefined | void>(); 8 | readonly onDidChangeTreeData: vscode.Event<ConanProfileItem | undefined | void> = this._onDidChangeTreeData.event; 9 | 10 | private conanApiManager: ConanAPIManager; 11 | 12 | public constructor(conanApiManager: ConanAPIManager) { 13 | this.conanApiManager = conanApiManager; 14 | } 15 | 16 | public refresh(): void { 17 | this._onDidChangeTreeData.fire(); 18 | } 19 | 20 | public getTreeItem(element: ConanProfileItem): vscode.TreeItem { 21 | return element; 22 | } 23 | 24 | public getChildren(element?: ConanProfileItem): ConanProfileItem[] { 25 | let profileList: string[] = []; 26 | let profileItemList: Array<ConanProfileItem> = []; 27 | 28 | if (this.conanApiManager.conanApi) { 29 | profileList = this.conanApiManager.conanApi.getProfiles(); 30 | 31 | for (let profile of profileList) { 32 | profileItemList.push(new ConanProfileItem(profile, vscode.TreeItemCollapsibleState.None)); 33 | } 34 | } 35 | 36 | return profileItemList; 37 | } 38 | 39 | public getChildrenString(): string[] { 40 | let childStringList = []; 41 | 42 | for (let child of this.getChildren()) { 43 | childStringList.push(child.label); 44 | } 45 | 46 | return childStringList; 47 | } 48 | } 49 | 50 | export class ConanProfileItem extends vscode.TreeItem { 51 | 52 | constructor( 53 | public readonly label: string, 54 | public readonly collapsibleState: vscode.TreeItemCollapsibleState) { 55 | 56 | super(label, collapsibleState); 57 | 58 | this.tooltip = `${this.label}`; 59 | 60 | this.command = { 61 | "title": "Conan Profile Selected", 62 | "command": "vsconan.explorer.treeview.profile.item.selected", 63 | }; 64 | } 65 | 66 | iconPath = { 67 | light: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'light', 'profile.png')), 68 | dark: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'dark', 'profile.png')) 69 | }; 70 | 71 | contextValue = 'profile'; 72 | } 73 | -------------------------------------------------------------------------------- /src/extension/ui/treeview/conanRecipeProvider.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as vscode from 'vscode'; 3 | import { ConanAPIManager } from '../../../conans/api/conanAPIManager'; 4 | import { ConanRecipe } from '../../../conans/model/conanRecipe'; 5 | import { SettingsPropertyManager } from '../../settings/settingsPropertyManager'; 6 | import { ConanAPI } from '../../../conans/api/base/conanAPI'; 7 | 8 | export class ConanRecipeNodeProvider implements vscode.TreeDataProvider<ConanRecipeItem> { 9 | 10 | private _onDidChangeTreeData: vscode.EventEmitter<ConanRecipeItem | undefined | void> = new vscode.EventEmitter<ConanRecipeItem | undefined | void>(); 11 | readonly onDidChangeTreeData: vscode.Event<ConanRecipeItem | undefined | void> = this._onDidChangeTreeData.event; 12 | 13 | private selectedRecipe: string | undefined = undefined; 14 | 15 | private conanApiManager: ConanAPIManager; 16 | private settingsPropertyManager: SettingsPropertyManager; 17 | 18 | public constructor(conanApiManager: ConanAPIManager, settingsPropertyManager: SettingsPropertyManager) { 19 | this.conanApiManager = conanApiManager; 20 | this.settingsPropertyManager = settingsPropertyManager; 21 | } 22 | 23 | public refresh(): void { 24 | this._onDidChangeTreeData.fire(); 25 | } 26 | 27 | public getTreeItem(element: ConanRecipeItem): vscode.TreeItem { 28 | return element; 29 | } 30 | 31 | public getChildren(element?: ConanRecipeItem): ConanRecipeItem[] { 32 | let recipeList: Array<ConanRecipe> = []; 33 | let recipeEditableList: Array<ConanRecipe> = []; 34 | let recipeItemList: Array<ConanRecipeItem> = []; 35 | 36 | if (this.conanApiManager.conanApi) { 37 | // Check the configuration, if the filter is set 38 | // If the filter is set, get the filte name. 39 | // Otherwise do as always 40 | // If filter is on, the editable package will not appear 41 | if (this.settingsPropertyManager.isRecipeFiltered()) { 42 | let filterKey: string = this.settingsPropertyManager.getRecipeFilterKey()!; 43 | 44 | recipeList = this.conanApiManager.conanApi.getRecipesByRemote(filterKey); 45 | } 46 | else { 47 | recipeList = this.conanApiManager.conanApi.getRecipes(); 48 | recipeEditableList = this.conanApiManager.conanApi.getEditablePackageRecipes(); 49 | } 50 | 51 | // Get the list of string from editable packages 52 | let editableRecipeStringList: Array<string> = []; 53 | 54 | for (let recipe of recipeEditableList) { 55 | editableRecipeStringList.push(recipe.name); 56 | recipeItemList.push(new ConanRecipeItem(recipe.name, vscode.TreeItemCollapsibleState.None, recipe)); 57 | } 58 | 59 | for (let recipe of recipeList) { 60 | // Basically even the package is editable, it will appear in the 'conan search' command 61 | // We dont want to have double name in the item list in the treeview, so we need to check if the package is already included in the editable list 62 | if (!editableRecipeStringList.includes(recipe.name)) { 63 | recipeItemList.push(new ConanRecipeItem(recipe.name, vscode.TreeItemCollapsibleState.None, recipe)); 64 | } 65 | } 66 | } 67 | 68 | return recipeItemList; 69 | } 70 | 71 | public getChildrenString(): string[] { 72 | let childStringList = []; 73 | 74 | for (let child of this.getChildren()) { 75 | childStringList.push(child.label); 76 | } 77 | 78 | return childStringList; 79 | } 80 | 81 | public setSelectedRecipe(recipe: string | undefined) { 82 | this.selectedRecipe = recipe; 83 | } 84 | 85 | public getSelectedRecipe(): string { 86 | return this.selectedRecipe!; 87 | } 88 | } 89 | 90 | export class ConanRecipeItem extends vscode.TreeItem { 91 | public model: ConanRecipe; 92 | 93 | constructor( 94 | public readonly label: string, 95 | public readonly collapsibleState: vscode.TreeItemCollapsibleState, 96 | model: ConanRecipe) { 97 | 98 | super(label, collapsibleState); 99 | 100 | this.model = model; 101 | 102 | this.tooltip = `${this.label}`; 103 | 104 | this.command = { 105 | "title": "Conan Recipe Selected", 106 | "command": "vsconan.explorer.treeview.recipe.item.selected", 107 | }; 108 | 109 | if (this.model.editable) { 110 | this.iconPath = { 111 | light: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'recipe_editable.png')), 112 | dark: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'recipe_editable.png')) 113 | }; 114 | 115 | this.contextValue = 'recipeEditable'; 116 | } 117 | else { 118 | this.iconPath = { 119 | light: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'recipe.png')), 120 | dark: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'recipe.png')) 121 | }; 122 | 123 | this.contextValue = 'recipe'; 124 | } 125 | } 126 | 127 | public isEditable(): boolean { 128 | return this.model.editable; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/extension/ui/treeview/conanRemoteProvider.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as vscode from 'vscode'; 3 | import { ConanAPIManager } from '../../../conans/api/conanAPIManager'; 4 | import { ConanRemote } from '../../../conans/model/conanRemote'; 5 | 6 | export class ConanRemoteNodeProvider implements vscode.TreeDataProvider<ConanRemoteItem> { 7 | 8 | private _onDidChangeTreeData: vscode.EventEmitter<ConanRemoteItem | undefined | void> = new vscode.EventEmitter<ConanRemoteItem | undefined | void>(); 9 | readonly onDidChangeTreeData: vscode.Event<ConanRemoteItem | undefined | void> = this._onDidChangeTreeData.event; 10 | 11 | private conanApiManager: ConanAPIManager; 12 | 13 | public constructor(conanApi: ConanAPIManager) { 14 | this.conanApiManager = conanApi; 15 | } 16 | 17 | public refresh(): void { 18 | this._onDidChangeTreeData.fire(); 19 | } 20 | 21 | public getTreeItem(element: ConanRemoteItem): vscode.TreeItem { 22 | return element; 23 | } 24 | 25 | public getChildren(element?: ConanRemoteItem): ConanRemoteItem[] { 26 | let remoteList: Array<ConanRemote> = []; 27 | let remoteItemList: Array<ConanRemoteItem> = []; 28 | 29 | if (this.conanApiManager.conanApi) { 30 | remoteList = this.conanApiManager.conanApi.getRemotes(); 31 | 32 | for (let remote of remoteList) { 33 | remoteItemList.push(new ConanRemoteItem(remote.name, vscode.TreeItemCollapsibleState.None, remote)); 34 | } 35 | } 36 | 37 | return remoteItemList; 38 | } 39 | 40 | public getChildrenString(): string[] { 41 | let childStringList = []; 42 | 43 | for (let child of this.getChildren()) { 44 | childStringList.push(child.label); 45 | } 46 | 47 | return childStringList; 48 | } 49 | } 50 | 51 | export class ConanRemoteItem extends vscode.TreeItem { 52 | 53 | constructor( 54 | public readonly label: string, 55 | public readonly collapsibleState: vscode.TreeItemCollapsibleState, 56 | public model: ConanRemote) { 57 | 58 | super(label, collapsibleState); 59 | 60 | this.model = model; 61 | 62 | this.tooltip = JSON.stringify(this.model, null, 4); 63 | 64 | this.command = { 65 | "title": "Conan Remote Selected", 66 | "command": "vsconan.explorer.treeview.remote.item.selected", 67 | }; 68 | 69 | this.setRemoteEnableIcon(this.model.enabled); 70 | } 71 | 72 | public setRemoteEnableIcon(state: boolean) { 73 | if (state) { // Remote is enabled 74 | this.iconPath = { 75 | light: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'light', 'remote_on.png')), 76 | dark: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'dark', 'remote_on.png')) 77 | }; 78 | } 79 | else { // Remote is disabled 80 | this.iconPath = { 81 | light: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'light', 'remote_off.png')), 82 | dark: vscode.Uri.file(path.join(__filename, '..', '..', '..', '..', '..', '..', 'resources', 'icon', 'dark', 'remote_off.png')) 83 | }; 84 | } 85 | } 86 | 87 | contextValue = 'remote'; 88 | } 89 | -------------------------------------------------------------------------------- /src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export const VSCONAN_FOLDER = ".vsconan"; 2 | export const CONFIG_FILE = "config.json"; 3 | export const TEMP_FOLDER = "temp"; -------------------------------------------------------------------------------- /test/conan/apiManager.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "../mocks/vscode"; 2 | import { ConanExecutionMode } from "../../src/conans/api/base/conanAPI"; 3 | import { ConanAPIManager } from "../../src/conans/api/conanAPIManager"; 4 | import { Conan1API } from "../../src/conans/conan/api/conanAPI"; 5 | import { Conan2API } from "../../src/conans/conan2/api/conanAPI"; 6 | 7 | jest.mock('vscode', () => vscode, { virtual: true }); 8 | 9 | let conanApiManager: ConanAPIManager; 10 | 11 | 12 | beforeAll(() => { 13 | conanApiManager = new ConanAPIManager(); 14 | }); 15 | 16 | describe("Initialization", () => { 17 | it("should have undefined conan API", () => { 18 | expect(conanApiManager.conanApi).toBe(undefined); 19 | }); 20 | 21 | it("should have empty string of conan version", () => { 22 | expect(conanApiManager.conanVersion).toBe(""); 23 | }); 24 | 25 | }); 26 | 27 | 28 | // conanVersion: string, pythonInterpreter: string, conanExecutable: string, conanExecutionMode: ConanExecutionMode 29 | describe("Setting API Instance", () => { 30 | 31 | it("should have conan API version 1", () => { 32 | conanApiManager.setApiInstance("1", "python", "conan", ConanExecutionMode.conan); 33 | expect(conanApiManager.conanApi).toBeInstanceOf(Conan1API); 34 | 35 | expect(conanApiManager.conanVersion).toBe("1"); 36 | }); 37 | 38 | it("should have conan API version 2", () => { 39 | conanApiManager.setApiInstance("2", "python", "conan", ConanExecutionMode.conan); 40 | expect(conanApiManager.conanApi).toBeInstanceOf(Conan2API); 41 | 42 | expect(conanApiManager.conanVersion).toBe("2"); 43 | }); 44 | 45 | it("should return undefined for Conan API object", () => { 46 | let invalidVersion: string = "3"; 47 | 48 | conanApiManager.setApiInstance(invalidVersion, "python", "conan", ConanExecutionMode.conan); 49 | expect(conanApiManager.conanApi).toBe(undefined); 50 | 51 | expect(conanApiManager.conanVersion).toBe(""); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /test/conan/commandBuilder/conan1Build.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "../../mocks/vscode"; 2 | 3 | import { ConfigCommandBuild } from "../../../src/conans/command/configCommand"; 4 | import { CommandBuilderConan1 } from "../../../src/conans/conan/commandBuilder"; 5 | import path = require("path"); 6 | 7 | jest.mock('vscode', () => vscode, { virtual: true }); 8 | 9 | let commandBuilder: CommandBuilderConan1; 10 | 11 | 12 | beforeAll(() => { 13 | commandBuilder = new CommandBuilderConan1(); 14 | }); 15 | 16 | 17 | describe("Conan 1 Build method", () => { 18 | 19 | it("should return conan build command with standard value", () => { 20 | 21 | let cmd = commandBuilder.buildCommandBuild("/home/user/ws", new ConfigCommandBuild()); 22 | 23 | expect(cmd?.length).toBe(9); 24 | 25 | let cmdString = cmd?.join(" "); 26 | 27 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -if ${JSON.stringify("/home/user/ws/install")} -bf ${JSON.stringify("/home/user/ws/build")} -pf ${JSON.stringify("/home/user/ws/package")} -sf ${JSON.stringify("/home/user/ws/source")}`); 28 | }); 29 | 30 | it("should return undefined due to missing conan recipe", () => { 31 | 32 | let conanBuild = new ConfigCommandBuild(); 33 | conanBuild.conanRecipe = ""; 34 | 35 | let cmd = commandBuilder.buildCommandBuild("/home/user/ws", conanBuild); 36 | 37 | expect(cmd).toBe(undefined); 38 | }); 39 | 40 | it("should return conan build command without install folder", () => { 41 | 42 | let conanBuild = new ConfigCommandBuild(); 43 | conanBuild.installFolder = ""; 44 | 45 | let cmd = commandBuilder.buildCommandBuild("/home/user/ws", conanBuild); 46 | 47 | expect(cmd?.length).toBe(7); 48 | 49 | let cmdString = cmd?.join(" "); 50 | 51 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -bf ${JSON.stringify("/home/user/ws/build")} -pf ${JSON.stringify("/home/user/ws/package")} -sf ${JSON.stringify("/home/user/ws/source")}`); 52 | }); 53 | 54 | it("should return conan build command without build folder", () => { 55 | 56 | let conanBuild = new ConfigCommandBuild(); 57 | conanBuild.buildFolder = ""; 58 | 59 | let cmd = commandBuilder.buildCommandBuild("/home/user/ws", conanBuild); 60 | 61 | expect(cmd?.length).toBe(7); 62 | 63 | let cmdString = cmd?.join(" "); 64 | 65 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -if ${JSON.stringify("/home/user/ws/install")} -pf ${JSON.stringify("/home/user/ws/package")} -sf ${JSON.stringify("/home/user/ws/source")}`); 66 | }); 67 | 68 | it("should return conan build command without package folder", () => { 69 | 70 | let conanBuild = new ConfigCommandBuild(); 71 | conanBuild.packageFolder = ""; 72 | 73 | let cmd = commandBuilder.buildCommandBuild("/home/user/ws", conanBuild); 74 | 75 | expect(cmd?.length).toBe(7); 76 | 77 | let cmdString = cmd?.join(" "); 78 | 79 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -if ${JSON.stringify("/home/user/ws/install")} -bf ${JSON.stringify("/home/user/ws/build")} -sf ${JSON.stringify("/home/user/ws/source")}`); 80 | }); 81 | 82 | it("should return conan build command without source folder", () => { 83 | 84 | let conanBuild = new ConfigCommandBuild(); 85 | conanBuild.sourceFolder = ""; 86 | 87 | let cmd = commandBuilder.buildCommandBuild("/home/user/ws", conanBuild); 88 | 89 | expect(cmd?.length).toBe(7); 90 | 91 | let cmdString = cmd?.join(" "); 92 | 93 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -if ${JSON.stringify("/home/user/ws/install")} -bf ${JSON.stringify("/home/user/ws/build")} -pf ${JSON.stringify("/home/user/ws/package")}`); 94 | }); 95 | 96 | it("should return conan build command without folder definitions", () => { 97 | 98 | let conanBuild = new ConfigCommandBuild(); 99 | conanBuild.installFolder = ""; 100 | conanBuild.sourceFolder = ""; 101 | conanBuild.packageFolder = ""; 102 | conanBuild.buildFolder = ""; 103 | 104 | let cmd = commandBuilder.buildCommandBuild("/home/user/ws", conanBuild); 105 | 106 | expect(cmd?.length).toBe(1); 107 | 108 | let cmdString = cmd?.join(" "); 109 | 110 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")}`); 111 | }); 112 | 113 | it("should return conan build command with additional args", () => { 114 | 115 | let conanBuild = new ConfigCommandBuild(); 116 | conanBuild.installFolder = ""; 117 | conanBuild.sourceFolder = ""; 118 | conanBuild.packageFolder = ""; 119 | conanBuild.buildFolder = ""; 120 | conanBuild.args = ["--some", "arg", "--another", "arg"]; 121 | 122 | let cmd = commandBuilder.buildCommandBuild("/home/user/ws", conanBuild); 123 | 124 | expect(cmd?.length).toBe(5); 125 | 126 | let cmdString = cmd?.join(" "); 127 | 128 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} --some arg --another arg`); 129 | }); 130 | 131 | }); -------------------------------------------------------------------------------- /test/conan/commandBuilder/conan1Create.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "../../mocks/vscode"; 2 | 3 | import { CommandBuilderConan1 } from "../../../src/conans/conan/commandBuilder"; 4 | import { ConfigCommandCreate } from "../../../src/conans/command/configCommand"; 5 | import path = require("path"); 6 | 7 | jest.mock('vscode', () => vscode, { virtual: true }); 8 | 9 | let commandBuilder: CommandBuilderConan1; 10 | 11 | 12 | beforeAll(() => { 13 | commandBuilder = new CommandBuilderConan1(); 14 | }); 15 | 16 | describe("Conan 1 Create method", () => { 17 | 18 | it("should return conan create command with standard value", () => { 19 | 20 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", new ConfigCommandCreate()); 21 | 22 | expect(cmd?.length).toBe(3); 23 | 24 | let cmdString = cmd?.join(" "); 25 | 26 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pr default`); 27 | }); 28 | 29 | it("should return conan create command with user and channel", () => { 30 | 31 | let conanCreate = new ConfigCommandCreate(); 32 | conanCreate.user = "user"; 33 | conanCreate.channel = "channel"; 34 | 35 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", conanCreate); 36 | 37 | expect(cmd?.length).toBe(4); 38 | 39 | let cmdString = cmd?.join(" "); 40 | 41 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} user/channel -pr default`); 42 | }); 43 | 44 | it("should return command string without user and channel due to missing user", () => { 45 | 46 | let conanCreate = new ConfigCommandCreate(); 47 | conanCreate.channel = "channel"; 48 | 49 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", conanCreate); 50 | 51 | expect(cmd?.length).toBe(3); 52 | 53 | let cmdString = cmd?.join(" "); 54 | 55 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pr default`); 56 | }); 57 | 58 | it("should return command string without user and channel due to missing channel", () => { 59 | 60 | let conanCreate = new ConfigCommandCreate(); 61 | conanCreate.user = "user"; 62 | 63 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", conanCreate); 64 | 65 | expect(cmd?.length).toBe(3); 66 | 67 | let cmdString = cmd?.join(" "); 68 | 69 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pr default`); 70 | }); 71 | 72 | it("should return undefined due to missing conan recipe", () => { 73 | 74 | let conanCreate = new ConfigCommandCreate(); 75 | conanCreate.conanRecipe = ""; 76 | 77 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", conanCreate); 78 | 79 | expect(cmd).toBe(undefined); 80 | }); 81 | 82 | it("should return command with user profile", () => { 83 | 84 | let conanCreate = new ConfigCommandCreate(); 85 | conanCreate.profile = "myProfile"; 86 | 87 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", conanCreate); 88 | 89 | expect(cmd?.length).toBe(3); 90 | 91 | let cmdString = cmd?.join(" "); 92 | 93 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pr myProfile`); 94 | }); 95 | 96 | it("should return command additional flags from args", () => { 97 | 98 | let conanCreate = new ConfigCommandCreate(); 99 | conanCreate.profile = "myProfile"; 100 | conanCreate.args = ["-pr:h", "host", "-pr:b", "build"]; 101 | 102 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", conanCreate); 103 | 104 | expect(cmd?.length).toBe(7); 105 | 106 | let cmdString = cmd?.join(" "); 107 | 108 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pr myProfile -pr:h host -pr:b build`); 109 | }); 110 | 111 | }); -------------------------------------------------------------------------------- /test/conan/commandBuilder/conan1Install.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "../../mocks/vscode"; 2 | 3 | import { CommandBuilderConan1 } from "../../../src/conans/conan/commandBuilder"; 4 | import { ConfigCommandInstall } from "../../../src/conans/command/configCommand"; 5 | import path = require("path"); 6 | 7 | jest.mock('vscode', () => vscode, { virtual: true }); 8 | 9 | let commandBuilder: CommandBuilderConan1; 10 | 11 | 12 | beforeAll(() => { 13 | commandBuilder = new CommandBuilderConan1(); 14 | }); 15 | 16 | 17 | describe("Conan 1 Install method", () => { 18 | 19 | it("should return conan install command with standard value", () => { 20 | 21 | let cmd = commandBuilder.buildCommandInstall("/home/user/ws", new ConfigCommandInstall()); 22 | 23 | expect(cmd?.length).toBe(5); 24 | 25 | let cmdString = cmd?.join(" "); 26 | 27 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pr default -if ${JSON.stringify("/home/user/ws/install")}`); 28 | }); 29 | 30 | it("should return conan install command with custom profile", () => { 31 | 32 | let conanInstall = new ConfigCommandInstall(); 33 | conanInstall.profile = "foo"; 34 | 35 | let cmd = commandBuilder.buildCommandInstall("/home/user/ws", conanInstall); 36 | 37 | expect(cmd?.length).toBe(5); 38 | 39 | let cmdString = cmd?.join(" "); 40 | 41 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pr foo -if ${JSON.stringify("/home/user/ws/install")}`); 42 | }); 43 | 44 | it("should return conan install command with user and channel", () => { 45 | 46 | let conanInstall = new ConfigCommandInstall(); 47 | conanInstall.profile = "foo"; 48 | conanInstall.user = "user"; 49 | conanInstall.channel = "channel"; 50 | 51 | let cmd = commandBuilder.buildCommandInstall("/home/user/ws", conanInstall); 52 | 53 | expect(cmd?.length).toBe(6); 54 | 55 | let cmdString = cmd?.join(" "); 56 | 57 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} user/channel -pr foo -if ${JSON.stringify("/home/user/ws/install")}`); 58 | }); 59 | 60 | it("should return conan install command without user and channel due to missing user", () => { 61 | 62 | let conanInstall = new ConfigCommandInstall(); 63 | conanInstall.profile = "foo"; 64 | conanInstall.channel = "channel"; 65 | 66 | let cmd = commandBuilder.buildCommandInstall("/home/user/ws", conanInstall); 67 | 68 | expect(cmd?.length).toBe(5); 69 | 70 | let cmdString = cmd?.join(" "); 71 | 72 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pr foo -if ${JSON.stringify("/home/user/ws/install")}`); 73 | }); 74 | 75 | it("should return undefined due to missing conan recipe", () => { 76 | 77 | let conanInstall = new ConfigCommandInstall(); 78 | conanInstall.conanRecipe = ""; 79 | 80 | let cmd = commandBuilder.buildCommandInstall("/home/user/ws", conanInstall); 81 | 82 | expect(cmd).toBe(undefined); 83 | }); 84 | 85 | it("should return conan install command with custom install folder", () => { 86 | 87 | let conanInstall = new ConfigCommandInstall(); 88 | conanInstall.installFolder = "bar"; 89 | 90 | let cmd = commandBuilder.buildCommandInstall("/home/user/ws", conanInstall); 91 | 92 | expect(cmd?.length).toBe(5); 93 | 94 | let cmdString = cmd?.join(" "); 95 | 96 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pr default -if ${JSON.stringify("/home/user/ws/bar")}`); 97 | }); 98 | 99 | it("should return conan install command with additional args", () => { 100 | let conanInstall = new ConfigCommandInstall(); 101 | conanInstall.installFolder = "bar"; 102 | conanInstall.args = ["-pr:b", "foo"]; 103 | 104 | let cmd = commandBuilder.buildCommandInstall("/home/user/ws", conanInstall); 105 | 106 | expect(cmd?.length).toBe(7); 107 | 108 | let cmdString = cmd?.join(" "); 109 | 110 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pr default -if ${JSON.stringify("/home/user/ws/bar")} -pr:b foo`); 111 | }); 112 | }); -------------------------------------------------------------------------------- /test/conan/commandBuilder/conan1Package.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "../../mocks/vscode"; 2 | 3 | import { CommandBuilderConan1 } from "../../../src/conans/conan/commandBuilder"; 4 | import { ConfigCommandPackage } from "../../../src/conans/command/configCommand"; 5 | import path = require("path"); 6 | 7 | jest.mock('vscode', () => vscode, { virtual: true }); 8 | 9 | let commandBuilder: CommandBuilderConan1; 10 | 11 | 12 | beforeAll(() => { 13 | commandBuilder = new CommandBuilderConan1(); 14 | }); 15 | 16 | 17 | describe("Conan 1 Package method", () => { 18 | 19 | it("should return conan package command with standard value", () => { 20 | 21 | let cmd = commandBuilder.buildCommandPackage("/home/user/ws", new ConfigCommandPackage()); 22 | 23 | expect(cmd?.length).toBe(9); 24 | 25 | let cmdString = cmd?.join(" "); 26 | 27 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -if ${JSON.stringify("/home/user/ws/install")} -bf ${JSON.stringify("/home/user/ws/build")} -pf ${JSON.stringify("/home/user/ws/package")} -sf ${JSON.stringify("/home/user/ws/source")}`); 28 | }); 29 | 30 | it("should return undefined due to missing conan recipe", () => { 31 | 32 | let conanPackage = new ConfigCommandPackage(); 33 | conanPackage.conanRecipe = ""; 34 | 35 | let cmd = commandBuilder.buildCommandPackage("/home/user/ws", conanPackage); 36 | 37 | expect(cmd).toBe(undefined); 38 | }); 39 | 40 | it("should return conan package command without install folder", () => { 41 | 42 | let conanPackage = new ConfigCommandPackage(); 43 | conanPackage.installFolder = ""; 44 | 45 | let cmd = commandBuilder.buildCommandPackage("/home/user/ws", conanPackage); 46 | 47 | expect(cmd?.length).toBe(7); 48 | 49 | let cmdString = cmd?.join(" "); 50 | 51 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -bf ${JSON.stringify("/home/user/ws/build")} -pf ${JSON.stringify("/home/user/ws/package")} -sf ${JSON.stringify("/home/user/ws/source")}`); 52 | }); 53 | 54 | it("should return conan package command without build folder", () => { 55 | 56 | let conanPackage = new ConfigCommandPackage(); 57 | conanPackage.installFolder = ""; 58 | conanPackage.buildFolder = ""; 59 | 60 | let cmd = commandBuilder.buildCommandPackage("/home/user/ws", conanPackage); 61 | 62 | expect(cmd?.length).toBe(5); 63 | 64 | let cmdString = cmd?.join(" "); 65 | 66 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pf ${JSON.stringify("/home/user/ws/package")} -sf ${JSON.stringify("/home/user/ws/source")}`); 67 | }); 68 | 69 | it("should return conan package command without package folder", () => { 70 | 71 | let conanPackage = new ConfigCommandPackage(); 72 | conanPackage.installFolder = ""; 73 | conanPackage.buildFolder = ""; 74 | conanPackage.packageFolder = ""; 75 | 76 | let cmd = commandBuilder.buildCommandPackage("/home/user/ws", conanPackage); 77 | 78 | expect(cmd?.length).toBe(3); 79 | 80 | let cmdString = cmd?.join(" "); 81 | 82 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -sf ${JSON.stringify("/home/user/ws/source")}`); 83 | }); 84 | 85 | it("should return conan package without arguments", () => { 86 | 87 | let conanPackage = new ConfigCommandPackage(); 88 | conanPackage.installFolder = ""; 89 | conanPackage.buildFolder = ""; 90 | conanPackage.packageFolder = ""; 91 | conanPackage.sourceFolder = ""; 92 | 93 | let cmd = commandBuilder.buildCommandPackage("/home/user/ws", conanPackage); 94 | 95 | expect(cmd?.length).toBe(1); 96 | 97 | let cmdString = cmd?.join(" "); 98 | 99 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")}`); 100 | }); 101 | }); -------------------------------------------------------------------------------- /test/conan/commandBuilder/conan1PackageExport.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "../../mocks/vscode"; 2 | 3 | import { CommandBuilderConan1 } from "../../../src/conans/conan/commandBuilder"; 4 | import { ConfigCommandPackageExport } from "../../../src/conans/command/configCommand"; 5 | import path = require("path"); 6 | 7 | jest.mock('vscode', () => vscode, { virtual: true }); 8 | 9 | let commandBuilder: CommandBuilderConan1; 10 | 11 | 12 | beforeAll(() => { 13 | commandBuilder = new CommandBuilderConan1(); 14 | }); 15 | 16 | 17 | describe("Conan 1 Package method", () => { 18 | 19 | it("should return conan package export command with standard value", () => { 20 | 21 | let cmd = commandBuilder.buildCommandPackageExport("/home/user/ws", new ConfigCommandPackageExport()); 22 | 23 | expect(cmd?.length).toBe(9); 24 | 25 | let cmdString = cmd?.join(" "); 26 | 27 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -if ${JSON.stringify("/home/user/ws/install")} -bf ${JSON.stringify("/home/user/ws/build")} -pf ${JSON.stringify("/home/user/ws/package")} -sf ${JSON.stringify("/home/user/ws/source")}`); 28 | }); 29 | 30 | it("should return undefined due to missing conan recipe", () => { 31 | 32 | let conanPackageExport = new ConfigCommandPackageExport(); 33 | conanPackageExport.conanRecipe = ""; 34 | 35 | let cmd = commandBuilder.buildCommandPackageExport("/home/user/ws", conanPackageExport); 36 | 37 | expect(cmd).toBe(undefined); 38 | }); 39 | 40 | it("should return conan package export command without install folder", () => { 41 | 42 | let conanPackageExport = new ConfigCommandPackageExport(); 43 | conanPackageExport.installFolder = ""; 44 | 45 | let cmd = commandBuilder.buildCommandPackageExport("/home/user/ws", conanPackageExport); 46 | 47 | expect(cmd?.length).toBe(7); 48 | 49 | let cmdString = cmd?.join(" "); 50 | 51 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -bf ${JSON.stringify("/home/user/ws/build")} -pf ${JSON.stringify("/home/user/ws/package")} -sf ${JSON.stringify("/home/user/ws/source")}`); 52 | }); 53 | 54 | it("should return conan package export command without build folder", () => { 55 | 56 | let conanPackageExport = new ConfigCommandPackageExport(); 57 | conanPackageExport.installFolder = ""; 58 | conanPackageExport.buildFolder = ""; 59 | 60 | let cmd = commandBuilder.buildCommandPackageExport("/home/user/ws", conanPackageExport); 61 | 62 | expect(cmd?.length).toBe(5); 63 | 64 | let cmdString = cmd?.join(" "); 65 | 66 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pf ${JSON.stringify("/home/user/ws/package")} -sf ${JSON.stringify("/home/user/ws/source")}`); 67 | }); 68 | 69 | it("should return conan package export command without source folder", () => { 70 | 71 | let conanPackageExport = new ConfigCommandPackageExport(); 72 | conanPackageExport.installFolder = ""; 73 | conanPackageExport.buildFolder = ""; 74 | conanPackageExport.sourceFolder = ""; 75 | 76 | let cmd = commandBuilder.buildCommandPackageExport("/home/user/ws", conanPackageExport); 77 | 78 | expect(cmd?.length).toBe(3); 79 | 80 | let cmdString = cmd?.join(" "); 81 | 82 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pf ${JSON.stringify("/home/user/ws/package")}`); 83 | }); 84 | 85 | it("should return conan package export command without package folder", () => { 86 | 87 | let conanPackageExport = new ConfigCommandPackageExport(); 88 | conanPackageExport.installFolder = ""; 89 | conanPackageExport.buildFolder = ""; 90 | conanPackageExport.sourceFolder = ""; 91 | conanPackageExport.packageFolder = ""; 92 | 93 | let cmd = commandBuilder.buildCommandPackageExport("/home/user/ws", conanPackageExport); 94 | 95 | expect(cmd?.length).toBe(1); 96 | 97 | let cmdString = cmd?.join(" "); 98 | 99 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")}`); 100 | }); 101 | 102 | }); -------------------------------------------------------------------------------- /test/conan/commandBuilder/conan1Source.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "../../mocks/vscode"; 2 | 3 | import { CommandBuilderConan1 } from "../../../src/conans/conan/commandBuilder"; 4 | import { ConfigCommandSource } from "../../../src/conans/command/configCommand"; 5 | import path = require("path"); 6 | 7 | jest.mock('vscode', () => vscode, { virtual: true }); 8 | 9 | let commandBuilder: CommandBuilderConan1; 10 | 11 | 12 | beforeAll(() => { 13 | commandBuilder = new CommandBuilderConan1(); 14 | }); 15 | 16 | 17 | describe("Conan 1 Source method", () => { 18 | 19 | it("should return conan source command with standard value", () => { 20 | 21 | let cmd = commandBuilder.buildCommandSource("/home/user/ws", new ConfigCommandSource()); 22 | 23 | expect(cmd?.length).toBe(5); 24 | 25 | let cmdString = cmd?.join(" "); 26 | 27 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -if ${JSON.stringify("/home/user/ws/install")} -sf ${JSON.stringify("/home/user/ws/source")}`); 28 | }); 29 | 30 | it("should return undefined due to missing conan recipe", () => { 31 | 32 | let conanSource = new ConfigCommandSource(); 33 | conanSource.conanRecipe = ""; 34 | 35 | let cmd = commandBuilder.buildCommandSource("/home/user/ws", conanSource); 36 | 37 | expect(cmd).toBe(undefined); 38 | }); 39 | }); -------------------------------------------------------------------------------- /test/conan/commandBuilder/conan2Create.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "../../mocks/vscode"; 2 | 3 | import { CommandBuilderConan2 } from "../../../src/conans/conan2/commandBuilder"; 4 | import { ConfigCommandCreate } from "../../../src/conans/command/configCommand"; 5 | import path = require("path"); 6 | 7 | jest.mock('vscode', () => vscode, { virtual: true }); 8 | 9 | let commandBuilder: CommandBuilderConan2; 10 | 11 | 12 | beforeAll(() => { 13 | commandBuilder = new CommandBuilderConan2(); 14 | }); 15 | 16 | describe("Conan 2 Create method", () => { 17 | 18 | it("should return conan create command with standard value", () => { 19 | 20 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", new ConfigCommandCreate()); 21 | 22 | expect(cmd?.length).toBe(3); 23 | 24 | let cmdString = cmd?.join(" "); 25 | 26 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} -pr default`); 27 | }); 28 | 29 | it("should return conan create command with user and channel", () => { 30 | 31 | let conanCreate = new ConfigCommandCreate(); 32 | conanCreate.user = "user"; 33 | conanCreate.channel = "channel"; 34 | 35 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", conanCreate); 36 | 37 | expect(cmd?.length).toBe(7); 38 | 39 | let cmdString = cmd?.join(" "); 40 | 41 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} --user user --channel channel -pr default`); 42 | }); 43 | 44 | it("should return command string with only channel", () => { 45 | 46 | let conanCreate = new ConfigCommandCreate(); 47 | conanCreate.channel = "channel"; 48 | 49 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", conanCreate); 50 | 51 | expect(cmd?.length).toBe(5); 52 | 53 | let cmdString = cmd?.join(" "); 54 | 55 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} --channel channel -pr default`); 56 | }); 57 | 58 | it("should return command string with only user", () => { 59 | 60 | let conanCreate = new ConfigCommandCreate(); 61 | conanCreate.user = "user"; 62 | 63 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", conanCreate); 64 | 65 | expect(cmd?.length).toBe(5); 66 | 67 | let cmdString = cmd?.join(" "); 68 | 69 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} --user user -pr default`); 70 | }); 71 | 72 | it("should return undefined due to missing conan recipe", () => { 73 | 74 | let conanCreate = new ConfigCommandCreate(); 75 | conanCreate.conanRecipe = ""; 76 | 77 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", conanCreate); 78 | 79 | expect(cmd).toBe(undefined); 80 | }); 81 | 82 | it("should return command additional flags from args", () => { 83 | 84 | let conanCreate = new ConfigCommandCreate(); 85 | conanCreate.user = "user"; 86 | conanCreate.channel = "channel"; 87 | conanCreate.profile = "myProfile"; 88 | conanCreate.args = ["-pr:h", "host", "-pr:b", "build"]; 89 | 90 | let cmd = commandBuilder.buildCommandCreate("/home/user/ws", conanCreate); 91 | 92 | expect(cmd?.length).toBe(11); 93 | 94 | let cmdString = cmd?.join(" "); 95 | 96 | expect(cmdString).toBe(`${JSON.stringify("/home/user/ws/conanfile.py")} --user user --channel channel -pr myProfile -pr:h host -pr:b build`); 97 | }); 98 | 99 | }); -------------------------------------------------------------------------------- /test/conan/commandBuilderFactory.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "../mocks/vscode"; 2 | 3 | import { CommandBuilderFactory } from "../../src/conans/command/commandBuilderFactory"; 4 | import { CommandBuilderConan1 } from "../../src/conans/conan/commandBuilder"; 5 | import { CommandBuilderConan2 } from "../../src/conans/conan2/commandBuilder"; 6 | 7 | jest.mock('vscode', () => vscode, { virtual: true }); 8 | 9 | describe("Test factory method for command builder", () => { 10 | it("should create command builder for conan version 1", () => { 11 | 12 | let cmdBuilder = CommandBuilderFactory.getCommandBuilder("1"); 13 | 14 | expect(cmdBuilder).toBeInstanceOf(CommandBuilderConan1); 15 | 16 | }); 17 | 18 | it("should create command builder for conan version 2", () => { 19 | 20 | let cmdBuilder = CommandBuilderFactory.getCommandBuilder("2"); 21 | 22 | expect(cmdBuilder).toBeInstanceOf(CommandBuilderConan2); 23 | 24 | }); 25 | 26 | it("should return undefined", () => { 27 | 28 | let cmdBuilder = CommandBuilderFactory.getCommandBuilder("3"); 29 | 30 | expect(cmdBuilder).toBe(undefined); 31 | 32 | }); 33 | 34 | 35 | 36 | }); -------------------------------------------------------------------------------- /test/conan/configCommand.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple test to check data integrity 3 | */ 4 | 5 | import { 6 | CommandContainer, 7 | ConfigCommand, ConfigCommandBuild, 8 | ConfigCommandCreate, ConfigCommandInstall, 9 | ConfigCommandPackage, ConfigCommandPackageExport, 10 | ConfigCommandSource 11 | } from "../../src/conans/command/configCommand"; 12 | 13 | describe("Conan Config Command basic class ", () => { 14 | 15 | it("should return original value", () => { 16 | let cfgCommand = new ConfigCommand(); 17 | expect(cfgCommand.name).toBe(""); 18 | expect(cfgCommand.description).toBe(""); 19 | expect(cfgCommand.detail).toBe(""); 20 | expect(cfgCommand.conanRecipe).toBe("conanfile.py"); 21 | }); 22 | 23 | }); 24 | 25 | describe("Conan Create", () => { 26 | 27 | it("should return original value", () => { 28 | let cfg = new ConfigCommandCreate(); 29 | 30 | expect(cfg.name).toBe("create"); 31 | expect(cfg.description).toBe("Create command"); 32 | expect(cfg.detail).toBe("Create command detail"); 33 | expect(cfg.profile).toBe("default"); 34 | expect(cfg.user).toBe(""); 35 | expect(cfg.channel).toBe(""); 36 | expect(cfg.args.length).toBe(0); 37 | 38 | }); 39 | 40 | }); 41 | 42 | describe("Conan Install", () => { 43 | 44 | it("should return original value", () => { 45 | let cfg = new ConfigCommandInstall(); 46 | 47 | expect(cfg.name).toBe("install"); 48 | expect(cfg.description).toBe("Install command"); 49 | expect(cfg.detail).toBe("Install command detail"); 50 | expect(cfg.profile).toBe("default"); 51 | expect(cfg.user).toBe(""); 52 | expect(cfg.channel).toBe(""); 53 | expect(cfg.args.length).toBe(0); 54 | 55 | }); 56 | 57 | }); 58 | 59 | describe("Conan Build", () => { 60 | 61 | it("should return original value", () => { 62 | let cfg = new ConfigCommandBuild(); 63 | 64 | expect(cfg.name).toBe("build"); 65 | expect(cfg.description).toBe("Build command"); 66 | expect(cfg.detail).toBe("Build command detail"); 67 | expect(cfg.installFolder).toBe("install"); 68 | expect(cfg.buildFolder).toBe("build"); 69 | expect(cfg.packageFolder).toBe("package"); 70 | expect(cfg.sourceFolder).toBe("source"); 71 | expect(cfg.args.length).toBe(0); 72 | 73 | }); 74 | 75 | }); 76 | 77 | describe("Conan Source", () => { 78 | 79 | it("should return original value", () => { 80 | let cfg = new ConfigCommandSource(); 81 | 82 | expect(cfg.name).toBe("source"); 83 | expect(cfg.description).toBe("Source command"); 84 | expect(cfg.detail).toBe("Source command detail"); 85 | expect(cfg.installFolder).toBe("install"); 86 | expect(cfg.sourceFolder).toBe("source"); 87 | expect(cfg.version).toBe(""); 88 | expect(cfg.user).toBe(""); 89 | expect(cfg.channel).toBe(""); 90 | expect(cfg.args.length).toBe(0); 91 | 92 | }); 93 | 94 | }); 95 | 96 | describe("Conan Package", () => { 97 | 98 | it("should return original value", () => { 99 | let cfg = new ConfigCommandPackage(); 100 | 101 | expect(cfg.name).toBe("pkg"); 102 | expect(cfg.description).toBe("Package command"); 103 | expect(cfg.detail).toBe("Package command detail"); 104 | expect(cfg.installFolder).toBe("install"); 105 | expect(cfg.buildFolder).toBe("build"); 106 | expect(cfg.packageFolder).toBe("package"); 107 | expect(cfg.sourceFolder).toBe("source"); 108 | }); 109 | 110 | }); 111 | 112 | describe("Conan Package Export", () => { 113 | 114 | it("should return original value", () => { 115 | let cfg = new ConfigCommandPackageExport(); 116 | 117 | expect(cfg.name).toBe("pkg_export"); 118 | expect(cfg.description).toBe("Package export command"); 119 | expect(cfg.detail).toBe("Package export command detail"); 120 | expect(cfg.installFolder).toBe("install"); 121 | expect(cfg.buildFolder).toBe("build"); 122 | expect(cfg.packageFolder).toBe("package"); 123 | expect(cfg.sourceFolder).toBe("source"); 124 | expect(cfg.user).toBe(""); 125 | expect(cfg.channel).toBe(""); 126 | expect(cfg.args.length).toBe(0); 127 | }); 128 | 129 | }); 130 | 131 | describe("Conan Command Container", () => { 132 | 133 | it("should initialize with empty list", () => { 134 | let ctn = new CommandContainer(); 135 | 136 | expect(ctn.create.length).toBe(0); 137 | expect(ctn.install.length).toBe(0); 138 | expect(ctn.build.length).toBe(0); 139 | expect(ctn.source.length).toBe(0); 140 | expect(ctn.pkg.length).toBe(0); 141 | expect(ctn.pkgExport.length).toBe(0); 142 | 143 | }); 144 | 145 | it("should have certain type of array", () => { 146 | let ctn = new CommandContainer(); 147 | 148 | expect(ctn.create.length).toBe(0); 149 | expect(ctn.install.length).toBe(0); 150 | expect(ctn.build.length).toBe(0); 151 | expect(ctn.source.length).toBe(0); 152 | expect(ctn.pkg.length).toBe(0); 153 | expect(ctn.pkgExport.length).toBe(0); 154 | }); 155 | }); -------------------------------------------------------------------------------- /test/conan/readEnv.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { execSync } from "child_process"; 3 | import { 4 | conan 5 | } from "../../src/utils/utils"; 6 | import path = require("path"); 7 | import fs = require('fs'); 8 | 9 | 10 | jest.mock('vscode', () => ({ 11 | workspace: { 12 | workspaceFolders: [{ uri: { fsPath: __dirname } }], 13 | } 14 | }), { virtual: true }); 15 | 16 | 17 | describe("readEnvFromConan ", () => { 18 | it("should return the env including PATH", async () => { 19 | execSync(`cd ${__dirname} && conan new basic --force`); 20 | let env = await conan.readEnvFromConan(conan.ConanEnv.buildEnv, "python", ["conanfile.py"]); 21 | expect(env).toBeInstanceOf(Array); 22 | expect(env[0]).toContain("PATH"); 23 | }); 24 | 25 | it("should contain custom settings", async () => { 26 | execSync(`cd ${__dirname} && conan new basic --force`); 27 | fs.appendFileSync(path.join(__dirname, "conanfile.py"), 28 | ' def configure(self):\n' + 29 | ' self.buildenv.define("FOO", "BAR")\n' + 30 | ' self.runenv.define("BAR", "BAZ")\n'); 31 | const buildenv = await conan.readEnvFromConan(conan.ConanEnv.buildEnv, "python", ["conanfile.py"]); 32 | expect(buildenv[0]).toStrictEqual(["FOO", "BAR"]); 33 | const runenv = await conan.readEnvFromConan(conan.ConanEnv.runEnv, "python", ["conanfile.py"]); 34 | expect(runenv[0]).toStrictEqual(["BAR", "BAZ"]); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/mocks/vscode.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; -------------------------------------------------------------------------------- /test/utils.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "./mocks/vscode"; 2 | jest.mock('vscode', () => vscode, { virtual: true }); 3 | 4 | import * as utils from "../src/utils/utils"; 5 | import * as os from "os"; 6 | import * as path from "path"; 7 | 8 | import { ConanProfileConfiguration } from "../src/extension/settings/model"; 9 | import { general, workspace } from "../src/utils/utils"; 10 | 11 | 12 | describe("General", () => { 13 | it("should convert plain object to class", () => { 14 | let plainObject: object = { 15 | "conanPythonInterpreter": "python", 16 | "conanExecutable": "conan", 17 | "conanExecutionMode": "conanExecutable", 18 | "conanVersion": "2", 19 | }; 20 | 21 | let profile: ConanProfileConfiguration = general.plainObjectToClass(ConanProfileConfiguration, plainObject); 22 | expect(profile.conanExecutable).toBe("conan"); 23 | expect(profile.conanVersion).toBe("2"); 24 | expect(profile.conanExecutionMode).toBe("conanExecutable"); 25 | expect(profile.conanPythonInterpreter).toBe("python"); 26 | expect(profile.conanUserHome).toBe(undefined); 27 | 28 | expect(profile.isValid()).toBeTruthy(); 29 | }); 30 | 31 | it("should ignore overpacked attributes from plain object", () => { 32 | let plainObject: object = { 33 | "conanPythonInterpreter": "python", 34 | "conanExecutable": "conan", 35 | "conanExecutionMode": "conanExecutable", 36 | "conanVersion": "2", 37 | "externalAttribute": "someValue", 38 | "unwantedAttribute": "123" 39 | }; 40 | 41 | let profile: ConanProfileConfiguration = general.plainObjectToClass(ConanProfileConfiguration, plainObject); 42 | 43 | expect(profile.conanExecutable).toBe("conan"); 44 | expect(profile.conanVersion).toBe("2"); 45 | expect(profile.conanExecutionMode).toBe("conanExecutable"); 46 | expect(profile.conanPythonInterpreter).toBe("python"); 47 | expect(profile.conanUserHome).toBe(undefined); 48 | }); 49 | }); 50 | 51 | describe("VSConan Utils", () => { 52 | it("should return home directory of vsconan in user home directory", () => { 53 | 54 | jest.mock("os"); 55 | 56 | const mockedHomedir = jest.spyOn(os, 'homedir').mockReturnValue(path.normalize('/home/user')); 57 | 58 | let homeDir = utils.vsconan.getVSConanHomeDir(); 59 | expect(homeDir).toEqual(path.normalize("/home/user/.vsconan")); 60 | 61 | expect(mockedHomedir).toHaveBeenCalled(); 62 | 63 | mockedHomedir.mockRestore(); 64 | }); 65 | 66 | it("should return path to temp directory of vsconan", () => { 67 | 68 | jest.mock("os"); 69 | const mockedHomedir = jest.spyOn(os, 'homedir').mockReturnValue(path.normalize('/home/user')); 70 | 71 | utils.vsconan.getVSConanHomeDir = jest.fn().mockImplementationOnce(() => "/home/user/.vsconan"); 72 | 73 | let tempDir = utils.vsconan.getVSConanHomeDirTemp(); 74 | 75 | expect(tempDir).toEqual(path.normalize("/home/user/.vsconan/temp")); 76 | 77 | mockedHomedir.mockRestore(); 78 | }); 79 | }); 80 | 81 | describe("Workspace", ()=>{ 82 | 83 | it("should get the absolute path", () => { 84 | let workspacePath = "/path/to/workspace"; 85 | 86 | let pathName = "/absolute/path/to/some/file"; 87 | 88 | let realPath = workspace.getAbsolutePathFromWorkspace(workspacePath, pathName); 89 | 90 | expect(realPath).toEqual(JSON.stringify(pathName)); 91 | 92 | }); 93 | 94 | it("should get the absolute path from the workspace", () => { 95 | let workspacePath = "/path/to/workspace"; 96 | 97 | let pathName = "relative/path/to/some/file"; 98 | 99 | let realPath = workspace.getAbsolutePathFromWorkspace(workspacePath, pathName); 100 | 101 | expect(realPath).toEqual(JSON.stringify("/path/to/workspace/relative/path/to/some/file")); 102 | 103 | }); 104 | 105 | it("should get the absolute path from the workspace (extra slash at the end)", () => { 106 | let workspacePath = "/path/to/workspace/"; 107 | 108 | let pathName = "relative/path/to/some/file"; 109 | 110 | let realPath = workspace.getAbsolutePathFromWorkspace(workspacePath, pathName); 111 | 112 | expect(realPath).toEqual(JSON.stringify("/path/to/workspace/relative/path/to/some/file")); 113 | 114 | }); 115 | 116 | it("should escape the white space with relative path", () => { 117 | let workspacePath = "/path/to/workspace/"; 118 | 119 | let pathName = "relative/path/to/some file"; 120 | 121 | let realPath = workspace.getAbsolutePathFromWorkspace(workspacePath, pathName); 122 | 123 | expect(realPath).toEqual(JSON.stringify("/path/to/workspace/relative/path/to/some file")); 124 | 125 | }); 126 | 127 | it("should get the absolute path with escaped whitespace", () => { 128 | let workspacePath = "/path/to/workspace"; 129 | 130 | let pathName = "/absolute/path/to/some file"; 131 | 132 | let realPath = workspace.getAbsolutePathFromWorkspace(workspacePath, pathName); 133 | 134 | expect(realPath).toEqual(JSON.stringify("/absolute/path/to/some file")); 135 | 136 | }); 137 | }); 138 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": ".", 11 | "strict": true, /* enable all strict type-checking options */ 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | "experimentalDecorators": true, 17 | "emitDecoratorMetadata": true, 18 | "skipLibCheck": true 19 | }, 20 | "exclude": [ 21 | "node_modules", 22 | ".vscode-test" 23 | ] 24 | } 25 | --------------------------------------------------------------------------------